1 Brief description of data set

A library of Rubisco variants based on a mutant variant of Gallionella sp. CbbM was designed. The CbbM variant is two amino acid exchanges away from the wild-type sequence and was identified as part of a small library which was used to establish the screening system. For reasons of simplicity, in the following, “WT” will refer to this variant, which is the “base” of the whole library. The large library consists of 67 positions, which were separately exchanged by any other possible amino acid and a combinatorial part, which is based on seven amino acid positions, which were exchanged, in a combinatorial fashion, by a few specific possible residues per position.

Both parts of the library were designed with the help of EVmutation and DeepSequence, which use phylogenetic information to predict beneficial amino acid exchanges. The exact manner how predictions were performed is described along with the small library the “base” mutant variant is derived from.

The complete library was transformed into a Synechocystis host strain, in which transcription of the endogenous Rubisco transcript can be inhibited by the induction of a CRISPR inhibition system. Each variant is represented by one or several clones, which can be distinguished on base of their N20 barcodes. The pooled library of these strains was grown in 8 replicates at different gas feed and light conditions in turbidostat mode for approximately 10 generations (CL_N2: constant light 300 µE, 5% CO2, 95% N2, 0% O2, CL_O2: constant light 300 µE, 5% CO2, 75% N2, 20% O2, LD: light-dark cycles, 5% CO2, 75% N2, 20% O2). By taking samples for Illumina sequencing regularly, we were able to track almost all variants present and assign fitness values.

Note that amino acid numbering in the following are based on the sequence including the N-Strep tag.

Fitness values are given as normalized values, i.e. a value of 1.0 equals the fitness of the base variant (actually the underlying CbbM variant which is two amino acid exchanges away from wild-type CbbM) and 0.0 equals the median value of K214 variants present in the data set. K214 is the carbamylated lysine which is essential for catalysis. Exchanges of this lysine residue should result in catalytically inactive enzyme. Mutant variants with worse fitness values than K214 substitutions can be considered catalytically inactive.

In a first step, all data of relevance will be loaded.

2 Diagnostic plots for cultivation

2.1 Sample-sample correlation

When clustering according to similarity, replicates from the same gas conditions have a tendency to cluster together, irrespective of their light condition (constant light or light-dark). This is also the case at generation 0 - even at generation 0, samples are more similar to other samples from the same gas feed than to generation 0 samples of the other round of cultivation (first round of cultivation was CL_N2; CL_O2 and LD were run in a second round of cultivations).

There is some clear separation between samples from earlier time points and samples from later time points.

df_counts <- tidyr::pivot_longer(count_matrix,
    cols = 2:ncol(count_matrix),
    names_to = "sample", values_to = "n_reads"
)

# sort
df_counts <- arrange(df_counts, sample)
df_counts <- left_join(df_samplesheet, df_counts)

df_correlation <- df_counts[,c("name", "sgRNA", "n_reads")] %>%
    tidyr::pivot_wider(names_from = "name", values_from = "n_reads") %>%
    dplyr::select(-c(1)) %>%
    cor()
annotation_days = data.frame(row.names=unique(row.names(df_correlation)), generation=as.character(c(rep(c("0", "3.7", "8.6", "10.1"), 2), rep(c("10.1", "8.6", "3.7", "0"), 2), rep(c("3.7", "10.1", "0", "8.6"), 2), "8.6", "0", "10.1", "3.7", "3.7", "0", "0", "5.4", "6.4", "7.7", "10.3", "0", "5.4", "0", "5.4", rep(c("0", "5.4", "6.4", "7.7", "10.3"), 5), rep(c("0", "4.9", "8.6", "12.5"),8))), condition=c(rep("CL_N2", 30), rep("LD", 34), rep("CL_O2", 32)), replicate=as.character(c(rep("1", 4), rep("2", 4), rep("3", 4), rep("4", 4), rep("5", 4), rep("6", 4), rep("7", 4), rep("8", 2), rep("1", 5), rep("2", 2), rep("3", 2), rep("4", 5), rep("5", 5), rep("6", 5), rep("7", 5), rep("8", 5), rep("1", 4), rep("2", 4), rep("3", 4), rep("4", 4), rep("5", 4), rep("6", 4), rep("7", 4), rep("8", 4))), gas_feed=c(rep("N2", 30), rep("O2", 66)))

# https://stackoverflow.com/questions/41628450/r-pheatmap-change-annotation-colors-and-prevent-graphics-window-from-popping-up
# choose gradient of colors for generations
# e.g. Tol from https://davidmathlogic.com/colorblind/#%23D81B60-%231E88E5-%23FFC107-%23004D40
okabe <- c("#f0e442ff", "#e69f00ff", "#d55e00ff", "#cc79a7ff", "#009e73ff", "#56b4e9ff", "#0072b2ff", "#aaaaaaff")
generations <- okabe[c(1, 2, 3, 3, 4, 4, 4, 5, 5, 5)]
# generations "0"    "3.7"  "8.6"  "10.1" "5.4"  "6.4"  "7.7"  "10.3" "4.9"  "12.5"
names(generations) <- c("0", "3.7", "4.9","5.4", "6.4", "7.7", "8.6", "10.1", "10.3", "12.5")
# replicates 1 to 8
rep <- okabe[1:8]
names(rep) <- as.character(1:8)
feed <- okabe[c(2, 7)]
names(feed) <- c("N2", "O2")
annotation_color_list <- list(condition=col_conditions, replicate=rep, generation=generations, gas_feed=feed)

# https://bioinformatics.stackexchange.com/questions/22502/manually-set-range-of-colour-scale-in-pheatmap-in-r
color.divisions <- 100

p <- pheatmap(df_correlation, display_numbers=TRUE, treeheight_col=0, cutree_rows = 6, cutree_cols = 6, annotation_row = annotation_days, annotation_colors = annotation_color_list, breaks = seq(0,1, length.out=(color.divisions + 1)), fontsize=6)

ggsave("../lfcSE_weighted_outputImages/pdf/correlation_samples_clustering_numbers.pdf", plot=p, width=20, height=20)

p <- pheatmap(df_correlation, display_numbers=FALSE, treeheight_col=0, cutree_rows = 6, cutree_cols = 6, annotation_row = annotation_days, annotation_colors = annotation_color_list, breaks = seq(0,1, length.out=(color.divisions + 1)), fontsize=6)
p

ggsave("../lfcSE_weighted_outputImages/png/correlation_samples_clustering.png", plot=p, width=11.5, height=10)
ggsave("../lfcSE_weighted_outputImages/pdf/correlation_samples_clustering.pdf", plot=p, width=11.5, height=10)

2.2 Check equal distribution in time 0 samples

For checking the distribution at time point 0, two t = 0 replicates from each condition were used.

count_matrix_time0 <- as.data.frame(data.frame(sgRNA=count_matrix$sgRNA, LD_1=count_matrix$LD1A1, LD_2=count_matrix$LD1A6, O2_1=count_matrix$O22A1, O2_2=count_matrix$O22A5, N2_1=count_matrix$highN4A1, N2_2=count_matrix$highN4A5))

row.names(count_matrix_time0) <- count_matrix_time0$sgRNA
count_matrix_time0$sgRNA <- NULL

design_matrix <- data.frame(group=rep("t0", 6))
row.names(design_matrix) = names(count_matrix_time0)

DESeq2 is used to normalize among the replicates.

ddstime0 <- DESeqDataSetFromMatrix(
  countData = count_matrix_time0,
  colData = design_matrix,
  design = ~ 1)
ddstime0 <- estimateSizeFactors(ddstime0)
counts_norm_ddstime0 <- as.data.frame(counts(ddstime0, normalized=TRUE))

When log10-transforming the x-scale of the mean of counts in the different samples, the distribution looks as if mean values were almost normally distributed. This is, if I am not mistaken, relatively typical for count data. (Count data does usually follow a Poisson distribution, which looks more “normal” when being log-transformed)

df_plot <- data.frame(mut=row.names(counts_norm_ddstime0), mean_count=apply(counts_norm_ddstime0, 1, mean))
p <- ggplot(df_plot, aes(x=mean_count)) + geom_histogram() + theme_light() + scale_x_log10()
p

The plot below shows mean and stdev of the count of 100 randomly picked barcodes present in the samples at time point 0 and confirms the histogram from above. There are outliers with many counts or almost no counts and a huge bulk of barcodes with a similar count. According to the histogram above, this bulk is approx. at a mean count of 10.

df_plot <- counts_norm_ddstime0[round(runif(n=100, min=1, max=nrow(counts_norm_ddstime0))),]
df_plot <- data.frame(mut=row.names(df_plot), mean_count=apply(df_plot, 1, mean), stdev=apply(df_plot,1,sd))
p <- ggplot(df_plot) + geom_bar(aes(x=reorder(mut, mean_count), y=mean_count), stat="identity") + geom_errorbar(aes(x=mut, ymin=mean_count-stdev, ymax=mean_count+stdev), width=0.4) + theme_light() + xlab("100 randomly picked barcodes") + ylab("Mean of normalized read counts") + labs(title="Randomly picked barcodes and their average abundance") + theme(axis.text.x=element_blank())
p
ggsave("../lfcSE_weighted_outputImages/pdf/time0_distribution.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/time0_distribution.png", plot=p)

Calculate the Gini index of a few of the samples, a value of 0 reflects complete equality, a value of 1 maximal inequality. Gini indices for all samples are also calculated as part of the whole nf-crispriscreen pipeline. With values of approx. 0.55 to 0.6, barcodes at time point 0 are not completely inequally distributed, but also far from perfect equality.

Gini(counts_norm_ddstime0$LD_1, unbiased=FALSE)
[1] 0.5701114
Gini(counts_norm_ddstime0$LD_2, unbiased=FALSE)
[1] 0.5651001
Gini(counts_norm_ddstime0$O2_1, unbiased=FALSE)
[1] 0.5664372
Gini(counts_norm_ddstime0$O2_2, unbiased=FALSE)
[1] 0.588848
Gini(counts_norm_ddstime0$N2_1, unbiased=FALSE)
[1] 0.5679795
Gini(counts_norm_ddstime0$N2_2, unbiased=FALSE)
[1] 0.5704703
Gini(df_plot$mean_count, unbiased=FALSE)
[1] 0.486499

3 Exploratory data analysis

3.1 Overview fitness distribution

Normalization: WT is set to “1”, and the median of K214 substitutions as “0”. For all three conditions, the data set shows a bimodal distribution. This is most pronounced for the continuous light, N2 feed condition.

plot_subset <- unique(fitness_data[,c("sgRNA_target", "norm", "p_fit_adj_WT", "condition")])

p <- ggplot(plot_subset, aes(x=norm, group=condition, fill=condition, color=condition, linetype=condition)) + geom_density(adjust=1.5, alpha=.4) + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank()) + scale_fill_manual(values=col_conditions, labels = c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + scale_color_manual(values=col_conditions, labels = c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + scale_linetype_manual(values=c("CL_N2"="solid", "CL_O2"="dashed", "LD"="dotted"), labels=c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + labs(title="Complete CbbM library", x="Normalized fitness value") 
p
ggsave("../lfcSE_weighted_outputImages/pdf/whole_library_densityplot.pdf", plot=p, width=11.5, height=8)
ggsave("../lfcSE_weighted_outputImages/png/whole_library_densityplot.png", plot=p, width=11.5, height=8)

for(cond in unique(plot_subset$condition)){
  print(cond)
  temp_subs <- subset(plot_subset, plot_subset$condition==cond)
  print(nrow(temp_subs))
  print("Above 1")
  print(nrow(subset(temp_subs, temp_subs$norm > 1)))
  print("Above 1, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm > 1))/nrow(temp_subs))
  print("Above 1 and significantly higher than WT")
  print(nrow(subset(temp_subs, temp_subs$norm > 1 & temp_subs$p_fit_adj_WT < 0.05)))
  print("Above 1 and significantly higher than WT, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm > 1 & temp_subs$p_fit_adj_WT < 0.05))/nrow(temp_subs))
  print("Below 0")
  print(nrow(subset(temp_subs, temp_subs$norm < 0)))
  print("Below 0, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm < 0))/nrow(temp_subs))
}
[1] "CL_N2"
[1] 13966
[1] "Above 1"
[1] 1351
[1] "Above 1, percentage"
[1] 0.09673493
[1] "Above 1 and significantly higher than WT"
[1] 180
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.01288844
[1] "Below 0"
[1] 159
[1] "Below 0, percentage"
[1] 0.01138479
[1] "CL_O2"
[1] 13966
[1] "Above 1"
[1] 1584
[1] "Above 1, percentage"
[1] 0.1134183
[1] "Above 1 and significantly higher than WT"
[1] 280
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.02004869
[1] "Below 0"
[1] 1655
[1] "Below 0, percentage"
[1] 0.1185021
[1] "LD"
[1] 13966
[1] "Above 1"
[1] 582
[1] "Above 1, percentage"
[1] 0.04167263
[1] "Above 1 and significantly higher than WT"
[1] 42
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.003007303
[1] "Below 0"
[1] 691
[1] "Below 0, percentage"
[1] 0.0494773

When only checking mutant variants present in the combinatorial library with more than a single amino acid exchange, we still observe a clear bimodal distribution very similar to the one of the complete data set.

plot_subset <- unique(subset(fitness_data, (fitness_data$category %in% c("combinatorial")) & fitness_data$number_muts > 1)[,c("sgRNA_target", "norm", "p_fit_adj_WT", "condition")])
length(unique(plot_subset$sgRNA_target))
[1] 12708
length(unique(plot_subset$sgRNA_target))/length(unique(fitness_data$sgRNA_target))
[1] 0.9099241
p <- ggplot(plot_subset, aes(x=norm, group=condition, fill=condition, color=condition, linetype=condition)) + geom_density(adjust=1.5, alpha=.4) + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank()) + scale_linetype_manual(values=c("CL_N2"="solid", "CL_O2"="dashed", "LD"="dotted"), labels=c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + scale_fill_manual(values=col_conditions, labels = c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + scale_color_manual(values=col_conditions, labels = c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + labs(title="Combinatorial CbbM library with more than one exchange", x="Normalized fitness value")
p
ggsave("../lfcSE_weighted_outputImages/pdf/combinatorial_w-oSingles_library_densityplot.pdf", plot=p, width=11.5, height=8)
ggsave("../lfcSE_weighted_outputImages/pdf/combinatorial_w-oSingles_library_densityplot.png", plot=p, width=11.5, height=8)

for(cond in unique(plot_subset$condition)){
  print(cond)
  temp_subs <- subset(plot_subset, plot_subset$condition==cond)
  print(nrow(temp_subs))
  print("Above 1")
  print(nrow(subset(temp_subs, temp_subs$norm > 1)))
  print("Above 1, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm > 1))/nrow(temp_subs))
  print("Above 1 and significantly higher than WT")
  print(nrow(subset(temp_subs, temp_subs$norm > 1 & temp_subs$p_fit_adj_WT < 0.05)))
  print("Above 1 and significantly higher than WT, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm > 1 & temp_subs$p_fit_adj_WT < 0.05))/nrow(temp_subs))
  print("Below 0")
  print(nrow(subset(temp_subs, temp_subs$norm < 0)))
  print("Below 0, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm < 0))/nrow(temp_subs))
}
[1] "CL_N2"
[1] 12708
[1] "Above 1"
[1] 1059
[1] "Above 1, percentage"
[1] 0.08333333
[1] "Above 1 and significantly higher than WT"
[1] 93
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.007318225
[1] "Below 0"
[1] 106
[1] "Below 0, percentage"
[1] 0.008341202
[1] "CL_O2"
[1] 12708
[1] "Above 1"
[1] 1248
[1] "Above 1, percentage"
[1] 0.09820585
[1] "Above 1 and significantly higher than WT"
[1] 144
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.01133144
[1] "Below 0"
[1] 1526
[1] "Below 0, percentage"
[1] 0.1200818
[1] "LD"
[1] 12708
[1] "Above 1"
[1] 352
[1] "Above 1, percentage"
[1] 0.02769909
[1] "Above 1 and significantly higher than WT"
[1] 3
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.0002360718
[1] "Below 0"
[1] 614
[1] "Below 0, percentage"
[1] 0.04831602
plot_subset <- unique(subset(fitness_data, fitness_data$category %in% c("saturational", "combiANDsatur"))[,c("sgRNA_target", "norm", "p_fit_adj_WT", "num_barcodes", "condition")])
length(unique(plot_subset$sgRNA_target))
[1] 1223
length(unique(plot_subset$sgRNA_target))/length(unique(fitness_data$sgRNA_target))
[1] 0.08756981
p <- ggplot(plot_subset, aes(x=norm, group=condition, fill=condition, color=condition, linetype=condition)) + geom_density(adjust=1.5, alpha=.4) + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank()) + scale_fill_manual(values=col_conditions, labels = c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + scale_color_manual(values=col_conditions, labels = c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed")) + labs(title="Saturational CbbM library", x="Normalized fitness value") + scale_linetype_manual(values=c("CL_N2"="solid", "CL_O2"="dashed", "LD"="dotted"), labels=c("Continuous light, N2 feed", "Continuous light, O2 feed", "Light-dark cycles, O2 feed"))
p
ggsave("../lfcSE_weighted_outputImages/pdf/saturational_library_densityplot.pdf", plot=p, width=11.5, height=8)
ggsave("../lfcSE_weighted_outputImages/pdf/saturational_library_densityplot.png", plot=p, width=11.5, height=8)

for(cond in unique(plot_subset$condition)){
  print(cond)
  temp_subs <- subset(plot_subset, plot_subset$condition==cond)
  print(nrow(temp_subs))
  print("Above 1")
  print(nrow(subset(temp_subs, temp_subs$norm > 1)))
  print("Above 1, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm > 1))/nrow(temp_subs))
  print("Above 1 and significantly higher than WT")
  print(nrow(subset(temp_subs, temp_subs$norm > 1 & temp_subs$p_fit_adj_WT < 0.05)))
  print("Above 1 and significantly higher than WT, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm > 1 & temp_subs$p_fit_adj_WT < 0.05))/nrow(temp_subs))
  print("Below 0")
  print(nrow(subset(temp_subs, temp_subs$norm < 0)))
  print("Below 0, percentage")
  print(nrow(subset(temp_subs, temp_subs$norm < 0))/nrow(temp_subs))
}
[1] "CL_N2"
[1] 1223
[1] "Above 1"
[1] 291
[1] "Above 1, percentage"
[1] 0.2379395
[1] "Above 1 and significantly higher than WT"
[1] 87
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.07113655
[1] "Below 0"
[1] 42
[1] "Below 0, percentage"
[1] 0.03434178
[1] "CL_O2"
[1] 1223
[1] "Above 1"
[1] 333
[1] "Above 1, percentage"
[1] 0.2722813
[1] "Above 1 and significantly higher than WT"
[1] 135
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.1103843
[1] "Below 0"
[1] 106
[1] "Below 0, percentage"
[1] 0.08667212
[1] "LD"
[1] 1223
[1] "Above 1"
[1] 230
[1] "Above 1, percentage"
[1] 0.1880621
[1] "Above 1 and significantly higher than WT"
[1] 39
[1] "Above 1 and significantly higher than WT, percentage"
[1] 0.0318888
[1] "Below 0"
[1] 63
[1] "Below 0, percentage"
[1] 0.05151267

When comparing fitness values between the combinatorial and saturational part directly, it becomes obvious that the saturational part contains, relatively speaking, more variants with functional Rubisco variants, i.e. the right peak of the bimodal distribution is higher than the left peak contrasting to the case in the combinatorial part of the library.

fitness_data_2 <- subset(fitness_data, fitness_data$category!="notExpected" & fitness_data$category!="WT")
fitness_data_2[fitness_data_2$category=="combiANDsatur",]$category <- "saturational"
fitness_data_2 <- unique(fitness_data_2[,c("sgRNA_target", "norm", "p_fit_adj_WT", "category", "condition")])
p <- ggplot(fitness_data_2, aes(x=norm, fill=category, color=category, linetype=category)) + geom_density(adjust=1.5, alpha=.4) + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank()) + scale_fill_manual(values=c(col_combi_sat), labels = c("Combinatorial", "Saturational")) + scale_color_manual(values=c(col_combi_sat), labels = c("Combinatorial", "Saturational")) + labs(x="Normalized fitness value") + scale_linetype_manual(values=c("combinatorial"="solid", "saturational"="dashed"), labels=c("Combinatorial", "Saturational")) + facet_wrap(~condition) + theme(panel.grid.minor = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8))
p
ggsave("../lfcSE_weighted_outputImages/pdf/density_compareCombiSatur.pdf", plot=p, width=10, height=4, units="cm")
ggsave("../lfcSE_weighted_outputImages/png/density_compareCombiSatur.png", plot=p)

3.2 Overview growth all variants

The mean of the log2FC of each variant present in the library is shown. Pink shows K214H, which is under all three conditions the K214 variant performing best, black the WT variant. Purple shows K214D and K214I, the two K214 variants performing worst. There is a clear separation of different variants, WT is clearly gaining in relative abundance and the investigated K214 variants are dropping. This confirms successful selection according to Rubisco activity.

fit_factor <- fitness_data
fit_factor$sgRNA_target <- factor(fit_factor$sgRNA_target, levels=c(unique(subset(fit_factor, !fit_factor$sgRNA_target %in% c("WT", "K214R"))$sgRNA_target), "WT", "K214R"))
p <- ggplot(fit_factor, aes(x=time, y=wmean_log2FoldChange, group=sgRNA_target, color=sgRNA_target, linewidth=sgRNA_target, alpha=sgRNA_target)) + geom_line() + scale_color_manual(values=c("K214R"="purple", "WT"="black"), na.value="lightgray") + theme_light() + xlab("Time (generations)") + ylab("log2FC(to g=0)") + scale_discrete_manual("linewidth", values=c("K214R"=0.3, "WT"=0.3), na.value=0.1) + scale_alpha_manual(values=c("K214R"=1, "WT"=1), na.value=0.5) + facet_wrap(~condition) + theme(panel.grid.minor = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8), legend.title=element_blank())
p
ggsave("../lfcSE_weighted_outputImages/pdf/all_sgRNAtargets_timeLinePlot.pdf", plot=p, height=1.5, width=5, units="in")
ggsave("../lfcSE_weighted_outputImages/png/all_sgRNAtargets_timeLinePlot.png", plot=p, height=1.5, width=5, units="in")

3.3 Plot WT and K214 variants

The 95% confidence interval for the weighted mean log2FC of all WT barcodes is relatively small, meaning that we can rely relatively well on the respective data. In total, 30 different barcodes for WT were taken into account. These were the highest 30 barcodes assigned to the WT variant present in the complete data set. The high number of WT barcodes as well as their high count numbers both contribute to a reliable estimate of the corresponding fitness.

WT_subset <- unique(subset(fitness_data, fitness_data$sgRNA_target=="WT")[,c("num_barcodes", "sd_log2FoldChange", "lfcSE", "time", "wmean_log2FoldChange", "sgRNA_target", "sgRNA", "log2FoldChange", "weight_lfcSE", "condition")])
p <- lineplot_CVinterval_severalColours_meanlog2(WT_subset) + labs(title="WT with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/WT_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/WT_timeLinePlot.png", plot=p)

Here, the log2FC charts of the different barcodes used for determining WT data are plotted. 95% confidence intervals are colored according to the standard error of the log2FC - the more transparent, the higher the standard error and the other way round. The barcodes deviating from the overall standard have higher standard errors than the barcodes which are close to the overall mean. In black, the overall trend is added.

It seems as if most of the 30 barcodes agreed well. Note that, for continuous light and an O2-containing feed, several barcodes go a bit down for the last time point. It seems as if selection was already lost then or at least got much weaker.

p <- lineplot_CVinterval_severalColours_log2(WT_subset) + labs(title="WT barcodes with 95% CI") + geom_line(aes(x=time, y=wmean_log2FoldChange, group=sgRNA_target), color="black")
p
ggsave("../lfcSE_weighted_outputImages/pdf/WT_barcodes_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/WT_barcodes_timeLinePlot.png", plot=p)


lineplot_severalColours_log2(WT_subset) + labs(title="WT barcodes") + geom_line(aes(x=time, y=wmean_log2FoldChange, group=sgRNA_target), color="black")

Here, all K214 mutant variants and their weighted mean log2FoldChange are plotted. All have a tendency to decrease in relative abundance. For continuous light and an O2-containing gas feed, the last time point looks as if selection got worse. Same also holds, to some degree, for the N2-containing feed. K214H goes pretty wild in the LD condition.

K214_subset <- unique(subset(fitness_data, grepl("K214", fitness_data$sgRNA_target))[,c("num_barcodes", "sd_log2FoldChange", "lfcSE", "time", "wmean_log2FoldChange", "sgRNA_target", "sgRNA", "log2FoldChange", "weight_lfcSE", "condition", "norm")])
p <- lineplot_CVinterval_severalColours_meanlog2(K214_subset) + labs(title="K214 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/K214_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/K214_variants_timeLinePlot.png", plot=p)


lineplot_severalColours_meanlog2(K214_subset) + labs(title="K214 variants")

K214H is the K214 variant with the highest fitness score under all three investigated conditions. Its fitness value was determined on basis of 3 barcodes, of which one shows a slower decrease than the other 2 (in CL_O2 and LD, it does not even show a decrease), but has a lower standard error. In black, the weighted mean log2FC is shown.

K214H_subset <- unique(subset(fitness_data, grepl("K214H", fitness_data$sgRNA_target))[,c("num_barcodes", "sd_log2FoldChange", "lfcSE", "time", "wmean_log2FoldChange", "sgRNA_target", "sgRNA", "log2FoldChange", "weight_lfcSE", "condition")])
p <- lineplot_CVinterval_severalColours_log2(K214H_subset) + labs(title="K214H barcodes with 95% CI") + geom_line(aes(x=time, y=wmean_log2FoldChange, group=sgRNA_target), color="black")
p
ggsave("../lfcSE_weighted_outputImages/pdf/K214H_barcodes_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/K214H_barcodes_timeLinePlot.png", plot=p)

Compare K214H to WT. In LD, due to the strongly rising barcode, K214H and WT are not significantly different (if we take 95% confidence intervals as measure for “Could it even be a significant difference”).

p <- lineplot_CVinterval_severalColours_meanlog2(bind_rows(WT_subset,K214H_subset)) + labs(title="K214H and WT")
p
ggsave("../lfcSE_weighted_outputImages/pdf/WT_K214H_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/WT_K214H_timeLinePlot.png", plot=p)

K214 variants K214D (LD), K214R (CL_N2) and K214E (CL_O2) were the ones which were used for median normalization (compare compare_weightingStrategies_2ndRound.html). All look as if they were good negative controls in subsequent plots. K214R is closest to a normalized fitness value of 0 of these three. When calculating the absolute distance to 0 of all variants in the normalized data set, K214L also seems to be a good candidate. Since K214R has a lower Gini index and a comparable absolute distance to 0 in all data sets, K214R will be plotted as negative control subsequently.

K214DRT_subset <- unique(subset(fitness_data, fitness_data$sgRNA_target %in% c("K214D", "K214R", "K214E", "K214L"))[,c("num_barcodes", "sd_log2FoldChange", "lfcSE", "time", "wmean_log2FoldChange", "sgRNA_target", "sgRNA", "log2FoldChange", "weight_lfcSE", "condition", "norm")])
p <- lineplot_CVinterval_severalColours_meanlog2(bind_rows(WT_subset,K214DRT_subset)) + labs(title="K214 variants and WT")
p
ggsave("../lfcSE_weighted_outputImages/pdf/WT_K214DRTL_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/WT_K214DRTL_timeLinePlot.png", plot=p)


pivot_wider(unique(K214DRT_subset[,c("sgRNA_target", "condition", "norm")]), values_from=norm, names_from=condition)

K214_set <- pivot_wider(unique(K214_subset[,c("sgRNA_target", "condition", "norm")]), values_from=norm, names_from=condition)
K214_set$sum <- apply(abs(K214_set[,c(2:4)]), 1, sum)
K214_set$gini <- apply(abs(K214_set[,c(2:4)]), 1, Gini)
K214_set[order(K214_set$sum),]

K214L_subset <- unique(subset(fitness_data, fitness_data$sgRNA_target %in% c("K214L", "K214R"))[,c("num_barcodes", "sd_log2FoldChange", "lfcSE", "time", "wmean_log2FoldChange", "sgRNA_target", "sgRNA", "log2FoldChange", "weight_lfcSE", "condition", "norm")])
lineplot_CVinterval_severalColours_meanlog2(bind_rows(WT_subset,K214L_subset)) + labs(title="K214L, K214R and WT")

neg_control_K214 <- "K214R"

All K214 variants and WT

p <- lineplot_severalColours_meanlog2(bind_rows(K214_subset, WT_subset)) + labs(title="K214 variants and WT") + scale_color_manual(values=c("WT"="black"), na.value="lightgray")
p
ggsave("../lfcSE_weighted_outputImages/pdf/WT_K214_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/WT_K214_variants_timeLinePlot.png", plot=p)

3.4 Barplots number mutations ~ fitness

When investigating all mutant variants present in the library, including the ones which occurred spontaneously, there is a clear trend observable that the more amino acid exchanges/mutations were introduced, the lower the mean fitness. This matches well with what was reported in literature before.

red_fitness <- unique(fitness_data[c("sgRNA_target", "mean_fitness", "condition", "category", "number_muts", "norm", "p_fit_adj_WT", "num_barcodes")])
p <- ggplot(red_fitness, aes(x=number_muts, y=norm, group=number_muts, colour=number_muts, fill=number_muts)) + geom_point(size=0.1, position=position_jitterdodge(dodge.width=0.9), color="#4a4a4aff") + theme_light() + stat_summary(fun="mean", geom="bar", alpha=0.3, position=position_dodge(width=0.9), linewidth=0, color="#4a4a4aff", fill="#4a4a4aff") + ylab("Weighted mean fitness") + facet_wrap(~condition)+ theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8))
p
ggsave("../lfcSE_weighted_outputImages/pdf/bar_fitness_data_numberMuts_complete.pdf", p, width=30, height=18, units="cm")
ggsave("../lfcSE_weighted_outputImages/png/bar_fitness_data_numberMuts_complete.png", p, width=30, height=18, units="cm")

When checking the whole “expected” library, i.e. the combination of saturational and combinatorial library, this trend is still observable.

expected_red_fitness <- subset(red_fitness, !red_fitness$category=="notExpected")
p <- ggplot(expected_red_fitness, aes(x=number_muts, y=norm, fill=number_muts, colour=number_muts, group=number_muts)) + stat_summary(fun="mean", geom="bar", alpha=0.3, position=position_dodge(width=0.9), linewidth=0, color="#4a4a4aff", fill="#4a4a4aff") + geom_point(size=0.05, alpha=0.7, position=position_jitterdodge(dodge.width=0.9), color="#4a4a4aff") + theme_light() + ylab("Normalized fitness score") + xlab("Number of intoduced amino acid exchanges") + theme(legend.position="none") + facet_wrap(~condition) + coord_cartesian(xlim=c(-0.2,7.2))+ theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8))
p
ggsave("../lfcSE_weighted_outputImages/pdf/bar_fitness_data_numberMuts_onlyExpected.pdf", p, width=5, height=2.4, units="in")
ggsave("../lfcSE_weighted_outputImages/png/bar_fitness_data_numberMuts_onlyExpected.png", p, width=5, height=3, units="in")

When only comparing the amino acid exchanges present in the combinatorial library, the overall trend that introducing another amino acid exchange decreases the fitness value becomes even clearer. At the same time, additionally introduced exchanges do not reduce the maximum fitness value.

combinatorial_wide <- subset(expected_red_fitness, !expected_red_fitness$category=="saturational")
lm_fitness_data <- lm(norm ~ number_muts, combinatorial_wide)
summary(lm_fitness_data)

Call:
lm(formula = norm ~ number_muts, data = combinatorial_wide)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.29518 -0.22402 -0.04572  0.18624  2.04364 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.921151   0.007505  122.73   <2e-16 ***
number_muts -0.096703   0.001414  -68.39   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3158 on 38188 degrees of freedom
Multiple R-squared:  0.1091,    Adjusted R-squared:  0.1091 
F-statistic:  4677 on 1 and 38188 DF,  p-value: < 2.2e-16
lm_fitness_data <- lm(norm ~ number_muts, subset(combinatorial_wide, combinatorial_wide$condition == "CL_N2"))
summary(lm_fitness_data)

Call:
lm(formula = norm ~ number_muts, data = subset(combinatorial_wide, 
    combinatorial_wide$condition == "CL_N2"))

Residuals:
     Min       1Q   Median       3Q      Max 
-0.81547 -0.20798 -0.04122  0.18888  1.16395 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.988449   0.011546   85.61   <2e-16 ***
number_muts -0.091316   0.002175  -41.98   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2805 on 12728 degrees of freedom
Multiple R-squared:  0.1216,    Adjusted R-squared:  0.1216 
F-statistic:  1762 on 1 and 12728 DF,  p-value: < 2.2e-16
lm_fitness_data <- lm(norm ~ number_muts, subset(combinatorial_wide, combinatorial_wide$condition == "CL_O2"))
summary(lm_fitness_data)

Call:
lm(formula = norm ~ number_muts, data = subset(combinatorial_wide, 
    combinatorial_wide$condition == "CL_O2"))

Residuals:
     Min       1Q   Median       3Q      Max 
-1.00643 -0.26879 -0.08333  0.21430  2.11737 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.968194   0.015364   63.02   <2e-16 ***
number_muts -0.113957   0.002895  -39.37   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.3733 on 12728 degrees of freedom
Multiple R-squared:  0.1086,    Adjusted R-squared:  0.1085 
F-statistic:  1550 on 1 and 12728 DF,  p-value: < 2.2e-16
lm_fitness_data <- lm(norm ~ number_muts, subset(combinatorial_wide, combinatorial_wide$condition == "LD"))
summary(lm_fitness_data)

Call:
lm(formula = norm ~ number_muts, data = subset(combinatorial_wide, 
    combinatorial_wide$condition == "LD"))

Residuals:
     Min       1Q   Median       3Q      Max 
-1.24018 -0.17207 -0.03075  0.14309  1.66143 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.806809   0.010654   75.73   <2e-16 ***
number_muts -0.084837   0.002007  -42.27   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2588 on 12728 degrees of freedom
Multiple R-squared:  0.1231,    Adjusted R-squared:  0.123 
F-statistic:  1786 on 1 and 12728 DF,  p-value: < 2.2e-16
p <- ggplot(combinatorial_wide, aes(x=number_muts, y=norm, fill=number_muts, colour=number_muts)) + stat_summary(fun="mean", geom="bar", alpha=0.3, position=position_dodge(width=0.9), linewidth=0, color="#4a4a4aff", fill="#4a4a4aff") + geom_point(size=0.1, position=position_jitterdodge(dodge.width=0.9), color="#4a4a4aff") + theme_light() + ylab("Normalized fitness score")+ theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + facet_wrap(~condition) + coord_cartesian(xlim=c(-0.2,7.2))
p
ggsave("../lfcSE_weighted_outputImages/pdf/bar_fitness_data_numberMuts_onlyCombi.pdf", p, width=30, height=18, units="cm")
ggsave("../lfcSE_weighted_outputImages/png/bar_fitness_data_numberMuts_onlyCombi.png", p, width=30, height=18, units="cm")

When only checking variants with fitness values significantly different to the “wild-type” sequence, this pattern gets dampened, even though there is still some trend in this direction observable with more and more variants showing relatively high scores.

combinatorial_wide <- subset(combinatorial_wide, combinatorial_wide$p_fit_adj_WT<0.05)

p <- ggplot(combinatorial_wide, aes(x=number_muts, y=norm, fill=number_muts, colour=number_muts)) + geom_point(size=0.1, position=position_jitterdodge(dodge.width=0.9)) + theme_light() + stat_summary(fun="mean", geom="bar", alpha=0.3, position=position_dodge(width=0.9), linewidth=0) + ylab("Weighted mean fitness") + facet_wrap(~condition) + coord_cartesian(xlim=c(0.8,7.2))+ theme(panel.grid.minor =element_blank(), legend.title = element_blank())+ theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8))
p
ggsave("../lfcSE_weighted_outputImages/pdf/bar_fitness_data_numberMuts_onlyCombi_onlySignificant.pdf", p, width=18, height=18, units="cm")
ggsave("../lfcSE_weighted_outputImages/png/bar_fitness_data_numberMuts_onlyCombi_onlySignificant.png", p, width=18, height=18, units="cm")

3.5 Correlation with different zero-shot predictors and similar

There are different ways of predicting the effect of higher order mutant variants. For designing the library, no prior knowledge was available. For this reason, zero-shot predictions using EVmutation and DeepSequence were performed. When data for single-site mutants is available, an additive model can be used that is assuming independence of different amino acid exchanges. Predictions of this model were calculated using python.

combi_sat_alpha <- c(combinatorial=0.2, saturational=0.2, WT=1.0)
combi_sat_shape <- c(combinatorial=16, saturational=16, WT=4)
combi_sat_size <- c(combinatorial=0.4, saturational=0.4, WT=0.8)
dotsize <- 0.02
pointshape <- 16
alphalevel <- 0.4
line_width_for_plots <- 0.4
red_fitness <- unique(fitness_data[c("sgRNA_target", "norm", "condition", "category", "EVcoup_predict", "DeepSeq_predict", "MSA_Transform", "additive_score", "proteinNPT_predict")])
red_fitness[red_fitness$category=="combiANDsatur",]$category <- "saturational"
red_fitness <- subset(red_fitness, red_fitness$category != "notExpected")
red_fitness[red_fitness$category=="WT",]$EVcoup_predict <- 0
red_fitness_noNA_DeepSeq <- subset(red_fitness, !is.na(red_fitness$DeepSeq_predict))

for(cat in c("combinatorial", "saturational")){
  print(cat)
  subset_forLoop <- subset(red_fitness_noNA_DeepSeq, red_fitness_noNA_DeepSeq$category==cat & red_fitness_noNA_DeepSeq$condition=="CL_N2")
  lm <- lm(DeepSeq_predict~EVcoup_predict, subset_forLoop)
  correlation <- cor.test(subset_forLoop$EVcoup_predict, subset_forLoop$DeepSeq_predict, method = 'spearman')
  print(correlation)
  correlation <- cor.test(subset_forLoop$EVcoup_predict, subset_forLoop$DeepSeq_predict, method = 'pearson')
  print(correlation)
}
[1] "combinatorial"

    Spearman's rank correlation rho

data:  subset_forLoop$EVcoup_predict and subset_forLoop$DeepSeq_predict
S = 1.4917e+11, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.5638764 


    Pearson's product-moment correlation

data:  subset_forLoop$EVcoup_predict and subset_forLoop$DeepSeq_predict
t = 81.392, df = 12706, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5738608 0.5967197
sample estimates:
      cor 
0.5854065 

[1] "saturational"
Warning: Cannot compute exact p-value with ties

    Spearman's rank correlation rho

data:  subset_forLoop$EVcoup_predict and subset_forLoop$DeepSeq_predict
S = 37504520, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.8137113 


    Pearson's product-moment correlation

data:  subset_forLoop$EVcoup_predict and subset_forLoop$DeepSeq_predict
t = 43.657, df = 1063, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.7786249 0.8217409
sample estimates:
      cor 
0.8012205 
scat <- ggplot(red_fitness_noNA_DeepSeq, aes(x=EVcoup_predict, y=DeepSeq_predict, color=category, alpha=category, linetype=category, shape=category, size=category)) + geom_point() + scale_shape_manual(values=combi_sat_shape) + scale_size_manual(values=combi_sat_size) + geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + theme_light() + xlab("EVcouplings fitness prediction") + ylab("DeepSequence fitness prediction") + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8), legend.title = element_blank())
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_DeepSeq_EVcoup.pdf", scat, width=2, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_DeepSeq_EVcoup.png", scat, width=2, height=2, units="in")

for(cat in c("combinatorial", "saturational")){
  print(cat)
  subset_forLoop <- subset(red_fitness_noNA_DeepSeq, red_fitness_noNA_DeepSeq$category==cat & red_fitness_noNA_DeepSeq$condition=="CL_N2")
  print(nrow(subset_forLoop))
  correlation <- cor.test(subset_forLoop$EVcoup_predict, subset_forLoop$MSA_Transform, method = 'spearman')
  print(correlation)
  correlation <- cor.test(subset_forLoop$EVcoup_predict, subset_forLoop$MSA_Transform, method = 'pearson')
  print(correlation)
}
[1] "combinatorial"
[1] 12708

    Spearman's rank correlation rho

data:  subset_forLoop$EVcoup_predict and subset_forLoop$MSA_Transform
S = 1.476e+11, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
     rho 
0.568465 


    Pearson's product-moment correlation

data:  subset_forLoop$EVcoup_predict and subset_forLoop$MSA_Transform
t = 82.411, df = 12706, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5787463 0.6014095
sample estimates:
      cor 
0.5901942 

[1] "saturational"
[1] 1065
Warning: Cannot compute exact p-value with ties

    Spearman's rank correlation rho

data:  subset_forLoop$EVcoup_predict and subset_forLoop$MSA_Transform
S = 55404058, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.7248026 


    Pearson's product-moment correlation

data:  subset_forLoop$EVcoup_predict and subset_forLoop$MSA_Transform
t = 34.666, df = 1063, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.6989539 0.7554535
sample estimates:
      cor 
0.7284399 
scat <- ggplot(red_fitness_noNA_DeepSeq, aes(x=EVcoup_predict, y=MSA_Transform, color=category, alpha=category, linetype=category, shape=category, size=category)) + geom_point() + scale_shape_manual(values=combi_sat_shape) + scale_size_manual(values=combi_sat_size) + geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + theme_light() + xlab("EVcouplings fitness prediction") + ylab("MSA Transform fitness prediction") + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8), legend.title = element_blank())
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_MSA_Transform_EVcoup.pdf", scat, width=2, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_MSA_Transform_EVcoup.png", scat, width=2, height=2, units="in")

red_fitness_noNA_additive <- subset(red_fitness, !is.na(red_fitness$additive_score))
# https://stackoverflow.com/questions/19699858/ggplot-adding-regression-line-equation-and-r2-with-facet
lm_eqn = function(df){
    m = lm(additive_score ~ norm, df);
    cor_form = cor.test(df$norm, df$additive_score, method = 'pearson')
    eq <- substitute("r ="~r, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2)))
    as.character(as.expression(eq));                 
}

eq <- ddply(red_fitness_noNA_additive,.(condition),lm_eqn)

p <- ggplot(data = red_fitness_noNA_additive, aes(x = norm, y = additive_score, color=category, alpha=category)) +
            geom_point(aes(size=category), shape=pointshape) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x,linetype="dashed", linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("Additive score") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_size_manual(values=combi_sat_size) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat)
scat = p + geom_text(data=eq,aes(x = -0.4, y = 1.8,label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_additive_combi.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_additive_combi.png", scat, width=6.8, height=2, units="in")

red_fitness_noNA_additive <- subset(red_fitness, !is.na(red_fitness$additive_score))
# https://stackoverflow.com/questions/19699858/ggplot-adding-regression-line-equation-and-r2-with-facet
lm_eqn = function(df){
    m = lm(additive_score ~ norm, df);
    cor_form = cor.test(df$norm, df$additive_score, method = 'spearman')
    eq <- substitute("rho ="~r, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2)))
    as.character(as.expression(eq));                 
}

eq <- ddply(red_fitness_noNA_additive,.(condition),lm_eqn)

p <- ggplot(data = red_fitness_noNA_additive, aes(x = norm, y = additive_score, color=category, alpha=category)) +
            geom_point(aes(size=category), shape=pointshape) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x,linetype="dashed", linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("Additive score") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_size_manual(values=combi_sat_size) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat)
scat = p + geom_text(data=eq,aes(x = -0.4, y = 1.8,label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_additive_combi_Spearman.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_additive_combi_Spearman.png", scat, width=6.8, height=2, units="in")

lm_eqn = function(df){
    m = lm(DeepSeq_predict ~ norm, df);
    cor_form = cor.test(df$norm, df$DeepSeq_predict, method = 'pearson')
    eq <- substitute("r ="~r*~cat2, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2),
             cat2 = unique(df$category2)))
    as.character(as.expression(eq));                 
}
red_fitness_noNA_DeepSeq$category2 <- red_fitness_noNA_DeepSeq$category
red_fitness_noNA_DeepSeq[red_fitness_noNA_DeepSeq$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(red_fitness_noNA_DeepSeq,.(condition, category2),lm_eqn)

red_fitness_noNA_DeepSeq_onlyComb_no4337 <- subset(red_fitness_noNA_DeepSeq, red_fitness_noNA_DeepSeq$category=="combinatorial" & !grepl("V337", red_fitness_noNA_DeepSeq$sgRNA_target))
correlation <- cor.test(red_fitness_noNA_DeepSeq_onlyComb_no4337$norm, red_fitness_noNA_DeepSeq_onlyComb_no4337$DeepSeq_predict, method = 'spearman')
Warning: Cannot compute exact p-value with ties
correlation

    Spearman's rank correlation rho

data:  red_fitness_noNA_DeepSeq_onlyComb_no4337$norm and red_fitness_noNA_DeepSeq_onlyComb_no4337$DeepSeq_predict
S = 3.6157e+11, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
       rho 
-0.1088873 
correlation <- cor.test(red_fitness_noNA_DeepSeq_onlyComb_no4337$norm, red_fitness_noNA_DeepSeq_onlyComb_no4337$DeepSeq_predict, method = 'pearson')
correlation

    Pearson's product-moment correlation

data:  red_fitness_noNA_DeepSeq_onlyComb_no4337$norm and red_fitness_noNA_DeepSeq_onlyComb_no4337$DeepSeq_predict
t = -12.552, df = 12505, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 -0.12882233 -0.09420665
sample estimates:
       cor 
-0.1115483 
scat <- ggplot(data = red_fitness_noNA_DeepSeq, aes(x = norm, y = DeepSeq_predict, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("DeepSequence prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-15, label=V1), vjust=c(0,1.2,0,1.2,0,1.2), parse = TRUE, inherit.aes=FALSE, size=8, size.unit="pt") + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_DeepSeq_norm.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_DeepSeq_norm.png", scat, width=6.8, height=2, units="in")

red_fitness_noNA_EVcoup <- subset(red_fitness, !is.na(red_fitness$EVcoup_predict))

lm_eqn = function(df){
    m = lm(EVcoup_predict ~ norm, df);
    cor_form = cor.test(df$norm, df$EVcoup_predict, method = 'pearson')
    eq <- substitute("r ="~r*~cat2, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2),
             cat2 = unique(df$category2)))
    as.character(as.expression(eq));                 
}
red_fitness_noNA_EVcoup$category2 <- red_fitness_noNA_EVcoup$category
red_fitness_noNA_EVcoup[red_fitness_noNA_EVcoup$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(red_fitness_noNA_EVcoup,.(condition, category2),lm_eqn)

red_fitness_noNA_EVcoup_no4337 <- subset(red_fitness_noNA_EVcoup, red_fitness_noNA_EVcoup$category=="combinatorial" & !grepl("V337", red_fitness_noNA_EVcoup$sgRNA_target))
correlation <- cor.test(red_fitness_noNA_EVcoup_no4337$norm, red_fitness_noNA_EVcoup_no4337$EVcoup_predict, method = 'spearman')
Warning: Cannot compute exact p-value with ties
correlation

    Spearman's rank correlation rho

data:  red_fitness_noNA_EVcoup_no4337$norm and red_fitness_noNA_EVcoup_no4337$EVcoup_predict
S = 2.5679e+11, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.2124506 
correlation <- cor.test(red_fitness_noNA_EVcoup_no4337$norm, red_fitness_noNA_EVcoup_no4337$EVcoup_predict, method = 'pearson')
correlation

    Pearson's product-moment correlation

data:  red_fitness_noNA_EVcoup_no4337$norm and red_fitness_noNA_EVcoup_no4337$EVcoup_predict
t = 23.733, df = 12505, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.1907747 0.2243161
sample estimates:
      cor 
0.2076064 
scat <- ggplot(data = red_fitness_noNA_EVcoup, aes(x = norm, y = EVcoup_predict, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("EVmutation prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-10, label=V1), vjust=c(0,1.2,0,1.2,0,1.2), parse = TRUE, inherit.aes=FALSE, size=8, size.unit = "pt") + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_norm_all_EVcoup.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_norm_all_EVcoup.png", scat, width=6.8, height=2, units="in")

lm_eqn = function(df){
    m = lm(MSA_Transform ~ norm, df);
    cor_form = cor.test(df$norm, df$MSA_Transform, method = 'pearson')
    eq <- substitute("r ="~r*~cat2, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2),
             cat2 = unique(df$category2)))
    as.character(as.expression(eq));                 
}
red_fitness_noNA_DeepSeq$category2 <- red_fitness_noNA_DeepSeq$category
red_fitness_noNA_DeepSeq[red_fitness_noNA_DeepSeq$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(red_fitness_noNA_DeepSeq,.(condition, category2),lm_eqn)

scat <- ggplot(data = red_fitness_noNA_DeepSeq, aes(x = norm, y = MSA_Transform, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("MSATransformer (ensemble) prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-10, label=V1), size=8, size.unit="pt", vjust=c(0,1.2,0,1.2,0,1.2), parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_MSA_Transform_norm.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_MSA_Transform_norm.png", scat, width=6.8, height=2, units="in")

df_proteinNPT <- subset(red_fitness, !is.na(red_fitness$proteinNPT_predict))

lm_eqn = function(df){
    m = lm(proteinNPT_predict ~ norm, df);
    cor_form = cor.test(df$norm, df$proteinNPT_predict, method = 'pearson')
    eq <- substitute("r ="~r*~cat2, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2),
             cat2 = unique(df$category2)))
    as.character(as.expression(eq));                 
}
df_proteinNPT$category2 <- df_proteinNPT$category
df_proteinNPT[df_proteinNPT$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(df_proteinNPT,.(condition, category2),lm_eqn)

scat <- ggplot(data = df_proteinNPT, aes(x = norm, y = proteinNPT_predict, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("ProteinNPT prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-2, label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_proteinNPT_norm.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_proteinNPT_norm.png", scat, width=6.8, height=2, units="in")


lm_eqn = function(df){
    m = lm(proteinNPT_predict ~ norm, df);
    cor_form = cor.test(df$norm, df$proteinNPT_predict, method = 'spearman')
    eq <- substitute("rho ="~r*~cat2, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r #Pearson's 
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2),
             cat2 = unique(df$category2)))
    as.character(as.expression(eq));                 
}
df_proteinNPT$category2 <- df_proteinNPT$category
df_proteinNPT[df_proteinNPT$sgRNA_target=="WT",]$category2 <- "saturational"
eq <- ddply(df_proteinNPT,.(condition, category2),lm_eqn)
Warning: Cannot compute exact p-value with tiesWarning: Cannot compute exact p-value with ties
scat <- ggplot(data = df_proteinNPT, aes(x = norm, y = proteinNPT_predict, color=category, alpha=category, linetype=category2, shape=category)) +
            geom_point(aes(size=category)) + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("ProteinNPT prediction") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 0.5, y =-2, label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_proteinNPT_norm_spearman.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_proteinNPT_norm_spearman.png", scat, width=6.8, height=2, units="in")

scat <- ggplot(df_proteinNPT, aes(x=proteinNPT_predict, y=MSA_Transform, color=category, alpha=category, linetype=category, shape=category, size=category)) + geom_point() + scale_shape_manual(values=combi_sat_shape) + scale_size_manual(values=combi_sat_size) + geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + theme_light() + xlab("ProteinNPT fitness prediction") + ylab("MSA Transform fitness prediction") + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8), legend.title = element_blank()) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_MSA_Transform_proteinNPT.pdf", scat, width=2, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_MSA_Transform_proteinNPT.png", scat, width=2, height=2, units="in")


scat <- ggplot(df_proteinNPT, aes(x=proteinNPT_predict, y=additive_score, color=category, alpha=category, linetype=category, shape=category, size=category)) + geom_point() + scale_shape_manual(values=combi_sat_shape) + scale_size_manual(values=combi_sat_size) + geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + theme_light() + xlab("ProteinNPT fitness prediction") + ylab("Additive score fitness prediction") + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + theme(panel.grid.minor =element_blank(), legend.position = "none", strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8), legend.title = element_blank()) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/scatter_additiveScore_proteinNPT.pdf", scat, width=2, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/scatter_additiveScore_proteinNPT.png", scat, width=2, height=2, units="in")

3.6 Correlation conservation to fitness

cor_subs <- subset(fitness_data, fitness_data$category=="saturational" | fitness_data$category=="combiANDsatur")
cor_subs[cor_subs$category=="combiANDsatur",]$category <- "saturational"
cor_subs <- subset(cor_subs, !is.na(cor_subs$conservationScore))

lm_eqn = function(df){
    m = lm(conservationScore ~ norm, df);
    cor_form = cor.test(df$norm, df$conservationScore, method = 'pearson')
    eq <- substitute("Pearson's r ="~r, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2)))
    as.character(as.expression(eq));                 
}
eq <- ddply(cor_subs,.(condition),lm_eqn)

scat <- ggplot(data = cor_subs, aes(x = norm, y = conservationScore, color=category, alpha=category, linetype=category)) +
            geom_point(aes(size=category, shape=category))  + scale_size_manual(values=combi_sat_size) + scale_shape_manual(values=combi_sat_shape) + 
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("Conservation score") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_alpha_manual(values=combi_sat_alpha) + scale_color_manual(values=col_combi_sat) + scale_linetype_manual(values=c("combinatorial"="dashed", "saturational"="twodash")) + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 1, y =0.9, label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat

ggsave("../lfcSE_weighted_outputImages/pdf/conservation_norm.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/conservation_norm.png", scat, width=6.8, height=2, units="in")

3.7 Correlation surface exposure to fitness

surface <- subset(fitness_data, fitness_data$category=="saturational" | fitness_data$category=="combiANDsatur")
surface[surface$category=="combiANDsatur",]$category <- "saturational"
surface <- subset(surface, !is.na(surface$relSAS))

lm_eqn = function(df){
    m = lm(relSAS ~ norm, df);
    cor_form = cor.test(df$norm, df$relSAS, method = 'pearson')
    eq <- substitute("Pearson's r ="~r, #italic(y) == a + b %.% italic(x)*" r2="~r2*" p= "~p*", Pearson's r="~r
         list(a = format(coef(m)[[1]], digits = 2), 
              b = format(coef(m)[[2]], digits = 2), 
             r2 = format(summary(m)$r.squared, digits = 3),
             r = format(cor_form$estimate[[1]], digits=2),
             p = format(cor_form$p.value[[1]], digits=2)))
    as.character(as.expression(eq));                 
}
eq <- ddply(surface,.(condition),lm_eqn)

scat <- ggplot(data = surface, aes(x = norm, y = relSAS, color=dimer)) +
            geom_point(size=dotsize, shape=pointshape, alpha=alphalevel) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linetype="dashed", linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("Relative solvent accessibility") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_color_manual(values=c("#93dfffff"), na.value="#777777") + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 1.0, y =-0.05, label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/relSAS_norm.pdf", scat, width=7, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/relSAS_norm.png", scat, width=30, height=18, units="cm")



surface_noDimer <- subset(surface, is.na(surface$dimer))
eq <- ddply(surface_noDimer,.(condition),lm_eqn)

scat <- ggplot(data = surface_noDimer, aes(x = norm, y = relSAS, color=dimer)) +
            geom_point(size=dotsize, shape=pointshape, alpha=alphalevel) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linetype="dashed", linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("Relative solvent accessibility") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_color_manual(values=c("#93dfffff"), na.value="#777777") + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 1.0, y =-0.05, label=V1), size=8, size.unit = "pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/relSAS_norm_noDimer.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/relSAS_norm_noDimer.png", scat, width=6.8, height=2, units="in")



surface_Dimer <- subset(surface, !is.na(surface$dimer))
eq <- ddply(surface_Dimer,.(condition),lm_eqn)

scat <- ggplot(data = surface_Dimer, aes(x = norm, y = relSAS, color=dimer)) +
            geom_point(size=dotsize, shape=pointshape, alpha=alphalevel) +
            geom_smooth(method = "lm", se=FALSE, color="black", formula = y ~ x, linetype="dashed", linewidth=line_width_for_plots) + xlab("Normalized fitness") + ylab("Relative solvent accessibility") + theme_light() + theme(panel.grid.minor =element_blank(), legend.title = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) + scale_color_manual(values=c("#93dfffff"), na.value="#777777") + facet_wrap(condition~.)
scat = scat + geom_text(data=eq,aes(x = 1.0, y =-0.05, label=V1), size=8, size.unit="pt", parse = TRUE, inherit.aes=FALSE) + facet_wrap(condition~.)
scat
ggsave("../lfcSE_weighted_outputImages/pdf/relSAS_norm_onlyDimer.pdf", scat, width=6.8, height=2, units="in")
ggsave("../lfcSE_weighted_outputImages/png/relSAS_norm_onlyDimer.png", scat, width=30, height=18, units="cm")

4 Comparing different conditions

A comparison between different conditions helps to identify variants which behave different in different conditions, e.g. because of an enhanced specificity or higher compatibility with light-dark cycles.

4.1 Compare N2 and O2 gas feeds

red_fitness <- unique(fitness_data[,c("sgRNA_target", "norm", "condition", "p_fit_adj_WT", "num_barcodes", "category")])
wide_fitness <- pivot_wider(red_fitness, values_from =c(norm,p_fit_adj_WT), names_from=condition)
wide_fitness$sgRNA_target <- factor(wide_fitness$sgRNA_target, levels=c(unique(subset(wide_fitness, wide_fitness$sgRNA_target != "WT"))$sgRNA_target, "WT"))
wide_fitness$WT_not_WT <- "notWT"
wide_fitness[wide_fitness$sgRNA_target=="WT",]$WT_not_WT <- "WT"

# set some plotting parameters
WT_shape <- c("notWT"=16, "WT"=4)
WT_alpha <- c("notWT"=0.2, "WT"=1.0)
WT_size <- c("notWT"=0.3, "WT"=0.5)
CLO2_CLN2_cutoff <- 0.25
barcode_cutoff <- 2
adjp_cutoff <- 0.05

# extract subsets from big data frames to test if significant differences
subset_signif_highO2 <- subset(fitness_data, fitness_data$sgRNA_target %in% subset(wide_fitness, wide_fitness$norm_CL_O2>1.0 & wide_fitness$p_fit_adj_WT_CL_O2<0.05)$sgRNA_target)
subset_signif_highLD <- subset(fitness_data, fitness_data$sgRNA_target %in% subset(wide_fitness, wide_fitness$norm_LD > wide_fitness$norm_CL_O2 & wide_fitness$norm_LD > 0.9)$sgRNA_target)

# prepare plotting, calculate lm to check distance from it (assuming linear regression gives better 1:1 than line through origin)
lm_CLO2_CLN2 <- lm(norm_CL_N2 ~ norm_CL_O2, wide_fitness)
summary(lm_CLO2_CLN2)

Call:
lm(formula = norm_CL_N2 ~ norm_CL_O2, data = wide_fitness)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.03132 -0.07939  0.01898  0.09884  0.88126 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 0.266196   0.001775   150.0   <2e-16 ***
norm_CL_O2  0.653940   0.003040   215.1   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.1505 on 13964 degrees of freedom
Multiple R-squared:  0.7681,    Adjusted R-squared:  0.7681 
F-statistic: 4.626e+04 on 1 and 13964 DF,  p-value: < 2.2e-16
correlation <- cor.test(wide_fitness$norm_CL_O2, wide_fitness$norm_CL_N2, method = 'spearman')
correlation

    Spearman's rank correlation rho

data:  wide_fitness$norm_CL_O2 and wide_fitness$norm_CL_N2
S = 7.8282e+10, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.8275755 
correlation <- cor.test(wide_fitness$norm_CL_O2, wide_fitness$norm_CL_N2, method = 'pearson')
correlation

    Pearson's product-moment correlation

data:  wide_fitness$norm_CL_O2 and wide_fitness$norm_CL_N2
t = 215.08, df = 13964, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8725266 0.8802195
sample estimates:
      cor 
0.8764289 
wide_fitness_CL_OL <- wide_fitness
wide_fitness_CL_OL$distance_lm <- wide_fitness_CL_OL$norm_CL_N2 - (lm_CLO2_CLN2$coefficients[1] + lm_CLO2_CLN2$coefficients[2] * wide_fitness_CL_OL$norm_CL_O2)
wide_fitness_CL_OL[order(wide_fitness_CL_OL$distance_lm, decreasing = FALSE),][1:10,]

# coloring according to differential expression
wide_fitness_CL_OL$diff <- "NO"
wide_fitness_CL_OL$diff[wide_fitness_CL_OL$distance_lm > 0] <- "CL_N2"
wide_fitness_CL_OL$diff[wide_fitness_CL_OL$distance_lm < 0] <- "CL_O2"
#wide_fitness_CL_OL$diff[wide_fitness_CL_OL$distance_lm > CLO2_CLN2_cutoff] <- "CL_N2" # in case we care for cut-off for minimal distance from linear regression
#wide_fitness_CL_OL$diff[wide_fitness_CL_OL$distance_lm < (-CLO2_CLN2_cutoff)] <- "CL_O2" # in case we care for cut-off for minimal distance from linear regression
wide_fitness_CL_OL[wide_fitness_CL_OL$sgRNA_target=="WT",]$diff <- "WT"
dotplot_colors <- c(col_conditions, "NO"="#d3d3d3b2", "orange", "WT"="black")

# prepare labels for plot
wide_fitness_CL_OL$delabel <- NA
wide_fitness_CL_OL$delabel[wide_fitness_CL_OL$diff !="NO"] <- as.character(wide_fitness_CL_OL$sgRNA_target[wide_fitness_CL_OL$diff != "NO"])

p <- ggplot(wide_fitness_CL_OL, aes(x=norm_CL_O2, y=norm_CL_N2, color=diff, shape=WT_not_WT)) + geom_point(aes(size=WT_not_WT, alpha=WT_not_WT), show.legend=FALSE) + scale_shape_manual(values=WT_shape) + scale_size_manual(values=WT_size) + scale_alpha_manual(values=WT_alpha) + theme_light() + labs(y="Weighted mean fitness value at continuous light, 5% CO2, 95% N2, 0% O2", x="Weighted mean fitness value at continuous light, 5% CO2, 75% N2, 20% O2") + theme(legend.position = "none", panel.grid.minor = element_blank()) + geom_abline(intercept=lm_CLO2_CLN2$coefficients[1],slope=lm_CLO2_CLN2$coefficients[2],linetype="dashed",color="black", linewidth=line_width_for_plots) + scale_colour_manual(values = dotplot_colors) + theme(panel.grid.minor = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) #+ xlim(-6, +7) +ylim(-6,+7)
ggsave(filename = "../lfcSE_weighted_outputImages/pdf/wfitness_CLN2_CLO2_withoutSignif_wo-labels.pdf", plot=p, width=4, height=4, units="cm")
ggsave(filename = "../lfcSE_weighted_outputImages/png/wfitness_CLN2_CLO2_withoutSignif_wo-labels.png", plot=p, width=4, height=4, units="cm")
p

Use Wilcoxon signed rank exact test to check if barcodes for the same variant behave significiantly different between the two tested conditions.

subset_signif_highO2 <- subset(subset_signif_highO2, subset_signif_highO2$condition %in% c("CL_N2", "CL_O2"))

get_controls <- function(cond_spec, sgRNA_spec){
  control_table <- subset_signif_highO2[subset_signif_highO2$condition != unique(cond_spec) & subset_signif_highO2$sgRNA_target == unique(sgRNA_spec) & subset_signif_highO2$time == 0,]
  control_table$fitness
}

subset_signif_highO2 <- dplyr::left_join(
  subset_signif_highO2,
  subset_signif_highO2 %>%
    dplyr::group_by(sgRNA_target, condition, time) %>%
    dplyr::summarize(
      .groups = "keep",
      # apply Wilcoxon rank sum test against other condition
      p_fitness_condition = stats::wilcox.test(
        x = fitness,
        y = get_controls(condition, sgRNA_target),
        paired = TRUE,
        alternative = "two.sided"
        )$p.value
      ),
  by = c("sgRNA_target", "condition", "time")
  ) 

subset_signif_highO2 <- subset_signif_highO2 %>%
  group_by(condition, time) %>%
  mutate(
    p_fitness_condition_adj = stats::p.adjust(p_fitness_condition, method = "BH")
    )

Plot part of data set which was tested for significant differences and highlight variants which were found to be significant.

subset_signif_highO2_red <- unique(subset_signif_highO2[,c("sgRNA_target", "norm", "condition", "num_barcodes", "p_fitness_condition_adj")])
subset_signif_highO2_red <- pivot_wider(subset_signif_highO2_red, values_from =c(norm,p_fitness_condition_adj), names_from=condition)

# alpha, size and labels according to significance
wide_fitness_CL_OL$signif <- "NO"
wide_fitness_CL_OL$signif[wide_fitness_CL_OL$sgRNA_target %in% unique(subset(subset_signif_highO2, subset_signif_highO2$p_fitness_condition_adj < adjp_cutoff))$sgRNA_target] <- "SIG"
wide_fitness_CL_OL$delabel <- NA
wide_fitness_CL_OL$delabel[wide_fitness_CL_OL$signif =="SIG"] <- as.character(wide_fitness_CL_OL$sgRNA_target[wide_fitness_CL_OL$signif =="SIG"])
wide_fitness_CL_OL[wide_fitness_CL_OL$sgRNA_target=="WT",]$signif <- "WT" # to ensure WT is visible
signif_size <- c("NO"=0.3, "SIG"=0.5, "WT"=0.5)
signif_alpha <- c("NO"=0.2, "SIG"=0.9, "WT"=1.0)

p <- ggplot(wide_fitness_CL_OL, aes(x=norm_CL_O2, y=norm_CL_N2, color=diff, label=delabel, shape=WT_not_WT)) + scale_shape_manual(values=WT_shape) + geom_point(aes(size=signif, alpha=signif), show.legend=FALSE) + scale_size_manual(values=signif_size) + scale_alpha_manual(values=signif_alpha) + theme_light() + labs(y="Weighted mean fitness value at continuous light, 5% CO2, 95% N2, 0% O2", x="Weighted mean fitness value at continuous light, 5% CO2, 75% N2, 20% O2") + theme(legend.position = "none", panel.grid.minor = element_blank()) + geom_abline(intercept=lm_CLO2_CLN2$coefficients[1],slope=lm_CLO2_CLN2$coefficients[2],linetype="dashed",color="black", linewidth=line_width_for_plots) + scale_colour_manual(values = dotplot_colors) + geom_text_repel(fontface="italic") + xlim(0.75, 2.5) +ylim(0.75,1.75)
p
ggsave(filename = "../lfcSE_weighted_outputImages/pdf/wfitness_CLN2_CLO2_Wilcox_signif.pdf", plot=p, width=12.5, height=12.5, units="cm")
ggsave(filename = "../lfcSE_weighted_outputImages/png/wfitness_CLN2_CLO2_Wilcox_signif.png", plot=p, width=12.5, height=12.5, units="cm")


wide_fitness_CL_OL[wide_fitness_CL_OL$signif=="SIG",][order(wide_fitness_CL_OL[wide_fitness_CL_OL$signif=="SIG",]$norm_CL_O2, decreasing=TRUE),]
possibly_specific_variants <- c("H358I", "K360I", "M328I", "Y333T", "H358Q", "Y322M", "D329T", "Y333V", "K99E", "M231F", "Y333I", "WT", neg_control_K214)
possibly_specific_variants <- c("H358I", "K360I", "M328I", "Y333T", "Y322M", "D329T", "K99E", "M231F", "WT", neg_control_K214)
different_colors_set <- c("H358I"="#332288ff", "K360I"="#117733ff", "M328I"="#44aa99ff", "Y333T"="#88cceeff", "Y322M"="#ddcc77ff", "D329T"="#cc6677ff", "K99E"="#aa4499ff", "M231F"="#882255ff", "WT"="black", neg_control_K214="darkgray")
possibly_specific_subset <- unique(subset(fitness_data, fitness_data$sgRNA_target %in% possibly_specific_variants))
possibly_specific_subset$sgRNA_target <- factor(possibly_specific_subset$sgRNA_target, levels=c(unique(subset(possibly_specific_subset, !possibly_specific_subset$sgRNA_target %in% c(neg_control_K214, "WT"))$sgRNA_target), neg_control_K214, "WT"))
p <- lineplot_CVinterval_severalColours_meanlog2(possibly_specific_subset) + labs(title="most different variants N2, O2 feeds") + scale_color_manual(values=different_colors_set) + scale_fill_manual(values=different_colors_set) #+ scale_linetype_manual(values=c("WT"="solid", possibly_specific_variants[9]=44, possibly_specific_variants[1]=88, possibly_specific_variants[2]=13, possibly_specific_variants[3]=1343, possibly_specific_variants[4]=131343, possibly_specific_variants[5]=73, possibly_specific_variants[6]=2262, possibly_specific_variants[7]=112233, possibly_specific_variants[8]=11223344))
p
ggsave("../lfcSE_weighted_outputImages/pdf/mostDifferent_CLN2_CLO2_timeLinePlot.pdf", plot=p, width=10, height=10)
ggsave("../lfcSE_weighted_outputImages/png/mostDifferent_CLN2_CLO2_variants_timeLinePlot.png", plot=p, width=10, height=10)

a <- pivot_wider(unique(subset(fitness_data, fitness_data$sgRNA_target %in% possibly_specific_variants)[,c("sgRNA_target", "condition", "norm", "p_fit_adj_WT", "num_barcodes")]), values_from=c("norm", "p_fit_adj_WT"), names_from=condition)
a$norm_multiply <- a$norm_CL_N2 * a$norm_CL_O2 * a$norm_LD
a[order(a$norm_multiply, decreasing = TRUE),]

4.2 Compare light-dark and continuous light at the same gas feed

lm_CLO2_LD <- lm(norm_LD ~ norm_CL_O2, wide_fitness)
summary(lm_CLO2_LD)

Call:
lm(formula = norm_LD ~ norm_CL_O2, data = wide_fitness)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.24578 -0.09276  0.00288  0.09357  1.31207 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) 0.143199   0.001874   76.41   <2e-16 ***
norm_CL_O2  0.607644   0.003211  189.25   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.159 on 13964 degrees of freedom
Multiple R-squared:  0.7195,    Adjusted R-squared:  0.7195 
F-statistic: 3.582e+04 on 1 and 13964 DF,  p-value: < 2.2e-16
correlation <- cor.test(wide_fitness$norm_CL_O2, wide_fitness$norm_LD, method = 'spearman')
correlation

    Spearman's rank correlation rho

data:  wide_fitness$norm_CL_O2 and wide_fitness$norm_LD
S = 7.9154e+10, p-value < 2.2e-16
alternative hypothesis: true rho is not equal to 0
sample estimates:
      rho 
0.8256552 
correlation <- cor.test(wide_fitness$norm_CL_O2, wide_fitness$norm_LD, method = 'pearson')
correlation

    Pearson's product-moment correlation

data:  wide_fitness$norm_CL_O2 and wide_fitness$norm_LD
t = 189.25, df = 13964, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8435058 0.8528124
sample estimates:
      cor 
0.8482246 
CL_LD_cutoff <- 0.32
barcode_cutoff <- 2
adjp_cutoff <- 0.05

wide_fitness_LD_OL <- wide_fitness

wide_fitness_LD_OL$distance_lm <- wide_fitness_LD_OL$norm_LD - (lm_CLO2_LD$coefficients[1] + lm_CLO2_LD$coefficients[2] * wide_fitness_LD_OL$norm_CL_O2)
wide_fitness_LD_OL[order(wide_fitness_LD_OL$distance_lm, decreasing = TRUE),][1:10,]

# coloring according to differential expression
wide_fitness_LD_OL$diff <- "NO"
wide_fitness_LD_OL$diff[wide_fitness_LD_OL$distance_lm > 0] <- "LD"
wide_fitness_LD_OL$diff[wide_fitness_LD_OL$distance_lm < 0] <- "CL_O2"
#wide_fitness_LD_OL$diff[wide_fitness_LD_OL$distance_lm > CL_LD_cutoff] <- "LD" # if using cut-off
#wide_fitness_LD_OL$diff[wide_fitness_LD_OL$distance_lm < (-CL_LD_cutoff)] <- "CL_O2" # if using cut-off
wide_fitness_LD_OL[wide_fitness_LD_OL$sgRNA_target=="WT",]$diff <- "WT"

p <- ggplot(wide_fitness_LD_OL, aes(x=norm_CL_O2, y=norm_LD, color=diff, shape=WT_not_WT)) + geom_point(aes(size=WT_not_WT, alpha=WT_not_WT), show.legend=FALSE) + scale_shape_manual(values=WT_shape) + scale_size_manual(values=WT_size) + scale_alpha_manual(values=WT_alpha) + theme_light() + labs(y="Weighted mean fitness value in light-dark cycles, 5% CO2, 75% N2, 20% O2", x="Weighted mean fitness value in continuous light, 5% CO2, 75% N2, 20% O2") + theme(legend.position = "none", panel.grid.minor = element_blank()) + geom_abline(intercept=lm_CLO2_LD$coefficients[1],slope=lm_CLO2_LD$coefficients[2],linetype="dashed",color="black")+ scale_colour_manual(values = dotplot_colors) + theme(panel.grid.minor = element_blank(), strip.background=element_rect(fill = NA, color = "white"), strip.text.x = element_text(size = 8, colour = "black", hjust = 0), axis.text=element_text(size=8), axis.title=element_text(size=8), legend.text=element_text(size=8)) #+ xlim(-6, +7) +ylim(-6,+7)  + geom_abline(intercept=lm_CLO2_LD$coefficients[1]-CL_LD_cutoff, slope=lm_CLO2_LD$coefficients[2], linetype="dashed", color="black") + geom_abline(intercept=lm_CLO2_LD$coefficients[1]+CL_LD_cutoff, slope=lm_CLO2_LD$coefficients[2], linetype="dashed", color="black") 
ggsave(filename = "../lfcSE_weighted_outputImages/pdf/wfitness_CLO2_LD_withoutSignif_withoutLabeling.pdf", plot=p, width=4, height=4, units="cm")
ggsave(filename = "../lfcSE_weighted_outputImages/png/wfitness_CLO2_LD_withoutSignif_withoutLabeling.png", plot=p, width=4, height=4, units="cm")
p

Run Wilcoxon test to check for significant differences between conditions.

subset_signif_highLD <- subset(subset_signif_highLD, subset_signif_highLD$condition %in% c("LD", "CL_O2") & subset_signif_highLD$time==0.0)

get_controls <- function(cond_spec, sgRNA_spec){
  control_table <- subset_signif_highLD[subset_signif_highLD$condition != unique(cond_spec) & subset_signif_highLD$sgRNA_target == unique(sgRNA_spec) & subset_signif_highLD$time == 0,]
  control_table$fitness
}

subset_signif_highLD <- dplyr::left_join(
  subset_signif_highLD,
  subset_signif_highLD %>%
    dplyr::group_by(sgRNA_target, condition, time) %>%
    dplyr::summarize(
      .groups = "keep",
      # apply Wilcoxon rank sum test against other condition
      p_fitness_condition = stats::wilcox.test(
        x = fitness,
        y = get_controls(condition, sgRNA_target),
        paired = TRUE,
        alternative = "two.sided"
        )$p.value
      ),
  by = c("sgRNA_target", "condition", "time")
  ) 

subset_signif_highLD <- subset_signif_highLD %>%
  group_by(condition, time) %>%
  mutate(
    p_fitness_condition_adj = stats::p.adjust(p_fitness_condition, method = "BH")
    )
subset_signif_highLD_red <- unique(subset_signif_highLD[,c("sgRNA_target", "norm", "condition", "num_barcodes", "p_fitness_condition_adj")])
subset_signif_highLD_red <- pivot_wider(subset_signif_highLD_red, values_from =c(norm,p_fitness_condition_adj), names_from=condition)

# labels, alpha and size according to significance
wide_fitness_LD_OL$signif <- "NO"
wide_fitness_LD_OL$signif[wide_fitness_LD_OL$sgRNA_target %in% unique(subset(subset_signif_highLD, subset_signif_highLD$p_fitness_condition_adj < 0.4))$sgRNA_target] <- "SIG"
wide_fitness_LD_OL$delabel <- NA
wide_fitness_LD_OL$delabel[wide_fitness_LD_OL$signif =="SIG"] <- as.character(wide_fitness_LD_OL$sgRNA_target[wide_fitness_LD_OL$signif =="SIG"])
wide_fitness_CL_OL[wide_fitness_CL_OL$sgRNA_target=="WT",]$signif <- "WT" # to ensure WT is visible

p <- ggplot(wide_fitness_LD_OL, aes(x=norm_CL_O2, y=norm_LD, color=diff, label=delabel, shape=WT_not_WT)) + scale_shape_manual(values=WT_shape) + geom_point(aes(size=signif, alpha=signif), show.legend=FALSE) + scale_size_manual(values=signif_size) + scale_alpha_manual(values=signif_alpha) + theme_light() + labs(y="Weighted mean fitness value at continuous light, 5% CO2, 95% N2, 0% O2", x="Weighted mean fitness value at continuous light, 5% CO2, 75% N2, 20% O2") + theme(legend.position = "none", panel.grid.minor = element_blank()) + geom_abline(intercept=lm_CLO2_LD$coefficients[1],slope=lm_CLO2_LD$coefficients[2],linetype="dashed",color="black") + scale_colour_manual(values = dotplot_colors) + geom_text_repel() #+ xlim(0.75, 2.5) +ylim(0.75,2.25)
p
ggsave(filename = "../lfcSE_weighted_outputImages/pdf/wfitness_LD_CLO2_Wilcox_signif.pdf", plot=p, width=12.5, height=12.5, units="cm")
ggsave(filename = "../lfcSE_weighted_outputImages/png/wfitness_LD_CLO2_Wilcox_signif.png", plot=p, width=12.5, height=12.5, units="cm")


wide_fitness_CL_OL[wide_fitness_LD_OL$signif=="SIG",][order(wide_fitness_LD_OL[wide_fitness_LD_OL$signif=="SIG",]$norm_LD, decreasing=TRUE),]
## coloring according to differential expression
wide_fitness_LD_OL$diff <- "NO"
wide_fitness_LD_OL$diff[wide_fitness_LD_OL$distance_lm > CL_LD_cutoff] <- "LD" # if using cut-off
wide_fitness_LD_OL$diff[wide_fitness_LD_OL$distance_lm < (-CL_LD_cutoff)] <- "CL_O2" # if using cut-off
wide_fitness_LD_OL[wide_fitness_LD_OL$sgRNA_target=="WT",]$diff <- "WT"

# labels, alpha and size according to significant difference to WT variant
wide_fitness_LD_OL$signif <- "NO"
wide_fitness_LD_OL$signif[wide_fitness_LD_OL$p_fit_adj_WT_CL_O2 < adjp_cutoff & wide_fitness_LD_OL$p_fit_adj_WT_LD < adjp_cutoff & (wide_fitness_LD_OL$diff=="LD" | wide_fitness_LD_OL$diff=="CL_O2") & wide_fitness_LD_OL$num_barcodes >= barcode_cutoff] <- "SIG"
signif_size <- c("NO"=0.2, "SIG"=0.3)
signif_alpha <- c("NO"=0.2, "SIG"=0.8)
wide_fitness_LD_OL$delabel <- as.character(wide_fitness_LD_OL$sgRNA_target)
wide_fitness_LD_OL$delabel[wide_fitness_LD_OL$signif !="SIG"] <- NA

p <- ggplot(wide_fitness_LD_OL, aes(x=norm_CL_O2, y=norm_LD, color=diff, shape=WT_not_WT, label=delabel)) + geom_point(aes(size=signif, alpha=signif), show.legend=FALSE) + scale_shape_manual(values=WT_shape) + scale_size_manual(values=signif_size) + scale_alpha_manual(values=signif_alpha) + theme_light() + labs(y="Weighted mean fitness value in light-dark cycles, 5% CO2, 75% N2, 20% O2", x="Weighted mean fitness value in continuous light, 5% CO2, 75% N2, 20% O2") + theme(legend.position = "none", panel.grid.minor = element_blank()) + geom_abline(intercept=lm_CLO2_LD$coefficients[1],slope=lm_CLO2_LD$coefficients[2],linetype="dashed",color="black")+ scale_colour_manual(values = dotplot_colors)  + geom_abline(intercept=lm_CLO2_LD$coefficients[1]-CL_LD_cutoff, slope=lm_CLO2_LD$coefficients[2], linetype="dashed", color="black") + geom_abline(intercept=lm_CLO2_LD$coefficients[1]+CL_LD_cutoff, slope=lm_CLO2_LD$coefficients[2], linetype="dashed", color="black") + geom_text_repel() #  + xlim(-6, +7) +ylim(-6,+7)
ggsave(filename = "../lfcSE_weighted_outputImages/pdf/wfitness_CLO2_LD_attempt_findDifferentVariants.pdf", plot=p, width=12.5, height=12.5, units="cm")
ggsave(filename = "../lfcSE_weighted_outputImages/png/wfitness_CLO2_LD_attempt_findDifferentVariants.png", plot=p, width=12.5, height=12.5, units="cm")
p


wide_fitness_LD_OL_sig <- subset(wide_fitness_LD_OL, wide_fitness_LD_OL$signif=="SIG")
wide_fitness_LD_OL_sig[order(wide_fitness_LD_OL_sig$distance_lm, decreasing = TRUE),][1:10,]
possibly_higher_inLD <- c("K456H", "G432S", "K99N", "H126G","WT", neg_control_K214)
different_colors_set <- c("K456H"="#332288ff", "G432S"="#117733ff", "H358M"="#44aa99ff", "H126N"="#88cceeff", "H126G"="#ddcc77ff", "K99N"="#cc6677ff", "WT"="black", neg_control_K214="darkgray")
single_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% possibly_higher_inLD)
p <- lineplot_CVinterval_severalColours_meanlog2(single_subset) + labs(title="most different variants") + scale_color_manual(values=different_colors_set) + scale_fill_manual(values=different_colors_set)
p
ggsave("../lfcSE_weighted_outputImages/pdf/mostDifferent_LD_CLO2_timeLinePlot.pdf", plot=p, width=10, height=10)
ggsave("../lfcSE_weighted_outputImages/png/mostDifferent_LD_CLO2_variants_timeLinePlot.png", plot=p, width=10, height=10)


unique(subset(fitness_data, fitness_data$sgRNA_target %in% possibly_higher_inLD)[,c("sgRNA_target", "condition", "norm", "p_fit_adj_WT", "num_barcodes")])

5 Generally interesting mutants

In the following, an attempt is made to pinpoint some interesting variants that are worth testing. The following chunk of code selects all variants which are performing well under all conditions and are significantly different to the base variant. Furthermore, only variants with at least two barcodes are taken into account.

columns <- c("sgRNA_target",  "condition", "norm", "p_fit_adj_WT", "num_barcodes", "number_muts")
fitness_data_goodVariants <- pivot_wider(unique(fitness_data[,columns]), values_from = c("norm", "p_fit_adj_WT"), names_from = condition)
fitness_data_goodVariants <- unique(subset(fitness_data_goodVariants, fitness_data_goodVariants$p_fit_adj_WT_CL_N2 < 0.05 & fitness_data_goodVariants$p_fit_adj_WT_CL_O2 < 0.05 & fitness_data_goodVariants$p_fit_adj_WT_LD < 0.05 & fitness_data_goodVariants$norm_CL_N2 > 1 & fitness_data_goodVariants$norm_CL_O2 > 1 & fitness_data_goodVariants$norm_LD > 1 & fitness_data_goodVariants$num_barcodes > 1))

length(unique(fitness_data_goodVariants$sgRNA_target))
[1] 23
length(unique(fitness_data_goodVariants$sgRNA_target))/length(unique(fitness_data$sgRNA_target))
[1] 0.001646857
fitness_data_goodVariants$combined_norms <- fitness_data_goodVariants$norm_CL_N2 * fitness_data_goodVariants$norm_CL_O2 * fitness_data_goodVariants$norm_LD
print(fitness_data_goodVariants[order(fitness_data_goodVariants$combined_norms, decreasing = TRUE),])
write_csv(fitness_data_goodVariants[order(fitness_data_goodVariants$combined_norms, decreasing = TRUE),], "../lfcSE_weighted_outputImages/goodVariants.csv")
highScoring_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H126N", "H126M", "W128I", "K360I", "K360M", "Q142D", "K233C", "H358C", "K360L", "H358L", "K99N", "K360C", "Y333V", "H126G", "M154E,K261A,Q406D,S446T", "Q142E", "M345Q", "F104V", "V98Q", "K261A,H265A,Q406D,S446I", "K99T", "K233L", "M345V", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(highScoring_subset) + labs(title="Fittest variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/highest_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/highest_variants_timeLinePlot.png", plot=p)

Partially, exchanges at the same amino acid position score quite similarly. We cannot distinguish their fitness on basis of our data.

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H126N", "H126M", "H126G", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit H126 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/H126_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/H126_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K360I", "K360M", "K360L", "K360C", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit K360 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/K360_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/K360_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H358C", "H358L", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit H358 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/H358_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/H358_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("Q142D", "Q142E", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit Q142 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/Q142_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/Q142_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("M345Q", "M345V", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit M345 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/M345_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/M345_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K233C", "K233L", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit K233 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/K233_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/K233_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K99N", "K99T", "WT", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit K99 variants with 95% CI")
p
ggsave("../lfcSE_weighted_outputImages/pdf/K99_subset_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/K99_subset_variants_timeLinePlot.png", plot=p)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("W128I", "Y333V", "F104V", "V98Q", "WT", neg_control_K214))
different_colors_set <- c("W128I"="#332288ff", "Y333V"="#117733ff", "V98Q"="#88cceeff", "F104V"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray") #different_colors_set <- c("W128I"="#332288ff", "Y333V"="#117733ff", "F104V"="#44aa99ff", "V98Q"="#88cceeff", "F104V"="#ddcc77ff", "K99N"="#cc6677ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Very fit variants with 95% CI") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p
ggsave("../lfcSE_weighted_outputImages/pdf/remaining_highScoring_variants_timeLinePlot.pdf", plot=p)
ggsave("../lfcSE_weighted_outputImages/png/remaining_highScoring_variants_timeLinePlot.png", plot=p)

6 Plot epistatic effects

6.1 General overview over epistasis in data set

From Miton et al., 2016 (https://onlinelibrary.wiley.com/doi/full/10.1002/pro.2876): “We calculated the fold change in enzyme fitness (F) provided by a mutation i on the wild-type background (ΔFwt,i=Fwt+i/Fwt) and the change caused by the same mutation on the intermediate variant j in the trajectory, (ΔFj,i=Fj+i/Fj) […] epistasis was determined by comparing the fold changes in the trajectory over the wild-type background (ΔFj,i/ΔFwt,i). For the sake of simplicity, we considered that ≥1.5-fold change is significant, and less than 1.5-fold change is neutral (or nearly neutral). […] Neutral designates mutations that show less than 1.5-fold change in enzyme fitness on both backgrounds (0.7 < [ΔFj,i and ΔFwt,i] < 1.5). (ii - v) Functional refers to non-neutral mutations that exhibit >1.5-fold improvement in either genetic background (ΔFj,i or ΔFwt,i > 1.5). Among functional mutations, ii) no epistasis refers to mutations that do not significantly alter the enzyme fitness depending on the background (mutations are additive, ΔFj,i/ΔFwt,i ∼ 1). (iii – iv) Positive epistasis applies to a mutation that becomes more beneficial when combined with prior substitutions on the trajectory, compared to its effect on the wild-type background (ΔFj,i/ΔFwt,i > 1.5). It can be divided in two subclasses: iii) Positive magnitude epistasis refers to cases where the effect is neutral or positive on the wild-type background and is further amplified on the trajectory (ΔFj,i ≫ ΔFwt, i > 0.7 or ΔFj, i ≫ ΔFwt, i > 1.5); and iv) Positive sign epistasis, which refers to mutations causing a deleterious effect on the wild-type background but a beneficial effect on the trajectory (ΔFwt, i <0.7 and ΔFj, i > 1.5). v) Negative epistasis applies to a mutation that becomes less beneficial on the trajectory background compared to its original effect on the wild type (ΔFj, i/ΔFwt, i <0.7).”

Identify cut-off values for good / bad variants - use adj. p values < 0.05 to do so, question “can I significantly distinguish this variant from the base variant?” Seems as if differences as small as a normed value of 1.1 are often already significant compared to the base variant - I assume a normed value of 1.25 is about right.

combinatorial_wide <- subset(combinatorial_wide, combinatorial_wide$p_fit_adj_WT<0.05) # in case things were changed above
a <- unique(subset(combinatorial_wide, combinatorial_wide$norm > 1.0)$norm)
a <- a[order(a)]
a[1:20]
 [1] 1.066177 1.077010 1.084487 1.089128 1.089533 1.099646 1.102634 1.103528 1.106683 1.108989 1.109216 1.113865 1.116721
[14] 1.118302 1.120763 1.121159 1.122081 1.123697 1.124440 1.127339

Also, differences as small as 0.9 compared to 1.0 can be distinguished - so I guess a good cut-off might be 0.8.

a <- unique(subset(combinatorial_wide, combinatorial_wide$norm < 1.0)$norm)
a <- a[order(a, decreasing=TRUE)]
a[1:20]
 [1] 0.9386771 0.9381779 0.9351133 0.9347565 0.9211815 0.9185717 0.9162180 0.9156449 0.9141355 0.9107536 0.9102129 0.9094565
[13] 0.9087976 0.9082542 0.9081407 0.9080229 0.9070452 0.9065860 0.9040132 0.9030803
summarize_dataframe <- unique(dplyr::summarize(.data=epistatic_table,
      .by = c(Exchange, Condition),
    # value in WT background
    value_background = unique(.data[["Exchange_value_and_ratio_in_WT"]]),
    # number neg. effect in higher order
    number_negative_exchanges = sum(.data[["Variant_ratio"]] < 0.8), # adjusted to 0.8 according to observations detailed above from originally 0.7 in paper
    # number neutral effect in higher order 
    number_neutral_exchanges = sum(.data[["Variant_ratio"]] > 0.8 & .data[["Variant_ratio"]] < 1.25), # adjusted to 0.8 and 1.2 from 0.7 and 1.5
    # number pos. effect in higher order
    number_positive_exchanges = sum(.data[["Variant_ratio"]] > 1.25), # adjusted from 1.5 in paper to 1.2 according to observations detailed above
    # no epistasis (0.7 < value < 1.5)
    number_no_epistasis = sum(.data[["ratio_WT_higherOrder"]] > 0.7 & .data[["ratio_WT_higherOrder"]] < 1.5),
    # positive epistasis (>1.5)
    number_positive_epistasis = sum(.data[["ratio_WT_higherOrder"]] > 1.5),
    # negative epistasis (<0.7)
    number_negative_epistasis = sum(.data[["ratio_WT_higherOrder"]] < 0.7),
    # total higher exchange
    number_higher_exchanges = n()
    ))
exchanges_dataset <- summarize_dataframe[,c("Exchange", "Condition", "number_negative_exchanges", "number_neutral_exchanges", "number_positive_exchanges")]
exchanges_dataset <- pivot_longer(exchanges_dataset, !c(Exchange, Condition), values_to="number", names_to="exchanges")

exchanges_dataset$Exchange <- factor(exchanges_dataset$Exchange, levels=rev(c("H141L", "H141V", "M154D", "M154E", "M154K", "M154S", "K261A", "K261D", "K261E", "K261F", "H265A", "H265E", "H265K", "H265R", "V337A", "V337S", "Q406D", "Q406E", "S446A", "S446I", "S446T")))

colors_exchanges <- brewer.pal(n = 9, name = "Blues")[c(3,6,9)]
names(colors_exchanges) <- c("number_positive_exchanges", "number_neutral_exchanges", "number_negative_exchanges")

p <- ggplot(exchanges_dataset) +
  geom_bar(aes(x = number, y = Exchange, fill = exchanges),
           position = "fill",
           stat = "identity")  + 
  scale_fill_manual(values=colors_exchanges) +
  facet_grid(~ Condition, switch = "x") + 
  theme_light() + 
  theme(strip.placement = "outside",
        strip.background = element_rect(fill = NA, color = "white"),
        panel.grid.minor = element_blank(), 
        axis.text=element_text(size=8), 
        axis.title=element_text(size=8), 
        legend.text=element_text(size=8),
        strip.text.x = element_text(size = 8, colour = "black"))
p 
ggsave("../lfcSE_weighted_outputImages/epistasis/Positive_negativeExchanges_higherOrdervariants.pdf", plot=p, width=7.5, height=3)
ggsave("../lfcSE_weighted_outputImages/epistasis/Positive_negativeExchanges_higherOrdervariants.png", plot=p, width=7.5, height=3)

epistasis_dataset <- summarize_dataframe[,c("Exchange", "Condition", "number_no_epistasis", "number_positive_epistasis", "number_negative_epistasis")]
epistasis_dataset <- pivot_longer(epistasis_dataset, !c(Exchange, Condition), values_to="number", names_to="epistasis")

epistasis_dataset$Exchange <- factor(epistasis_dataset$Exchange, levels=rev(c("H141L", "H141V", "M154D", "M154E", "M154K", "M154S", "K261A", "K261D", "K261E", "K261F", "H265A", "H265E", "H265K", "H265R", "V337A", "V337S", "Q406D", "Q406E", "S446A", "S446I", "S446T")))

colors_epistasis <- brewer.pal(n = 9, name = "Blues")[c(3,6,9)]
names(colors_epistasis) <- c("number_positive_epistasis", "number_no_epistasis", "number_negative_epistasis")

p <- ggplot(epistasis_dataset) +
  geom_bar(aes(x = number, y = Exchange, fill = epistasis),
           position = "fill",
           stat = "identity") +
  scale_fill_manual(values=colors_epistasis) +
  facet_grid(~ Condition, switch = "x") + 
  theme_light() + 
  theme(strip.placement = "outside",
        strip.background = element_rect(fill = NA, color = "white"),
        panel.grid.minor = element_blank(), 
        axis.text=element_text(size=8), 
        axis.title=element_text(size=8), 
        legend.text=element_text(size=8),
        strip.text.x = element_text(size = 8, colour = "black"))
p
ggsave("../lfcSE_weighted_outputImages/epistasis/Positive_negativeEpistasis_higherOrdervariants.pdf", plot=p, width=7.5, height=3)
ggsave("../lfcSE_weighted_outputImages/epistasis/Positive_negativeEpistasis_higherOrdervariants.png", plot=p, width=7.5, height=3)

6.2 Examples of epistasis

epistatic_table_wide <- pivot_wider(epistatic_table[,c("Condition", "Exchange_value_and_ratio_in_WT", "Exchange", "Variant", "Variant_value", "ratio_WT_higherOrder")], names_from=c("Condition"), values_from=c("Variant_value", "ratio_WT_higherOrder", "Exchange_value_and_ratio_in_WT"))
epistatic_table_wide <- subset(epistatic_table_wide, epistatic_table_wide$ratio_WT_higherOrder_CL_N2>1.5 & epistatic_table_wide$ratio_WT_higherOrder_CL_O2 > 1.5 & epistatic_table_wide$ratio_WT_higherOrder_LD > 1.5 & epistatic_table_wide$Variant_value_CL_N2 > 1.0 & epistatic_table_wide$Variant_value_CL_O2 > 1.0 & epistatic_table_wide$Variant_value_LD > 1.0)
nrow(epistatic_table_wide)
[1] 14
epistatic_table_wide$ep_product <- epistatic_table_wide$ratio_WT_higherOrder_CL_N2 * epistatic_table_wide$ratio_WT_higherOrder_CL_O2 * epistatic_table_wide$ratio_WT_higherOrder_LD
epistatic_table_wide[order(epistatic_table_wide$ep_product, decreasing=TRUE),]

Check again if any of the “good, recovered” strains are just an artefact !

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("M154K", "M154K,H265E,Q406E,S446T", "H265E,Q406E,S446T", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("M154K"="#88cceeff", "M154K,H265E,Q406E,S446T"="#117733ff", "H265E,Q406E,S446T"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving M154K") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K261D", "K261D,H265E,Q406E,S446T", "H265E,Q406E,S446T", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("K261D"="#88cceeff", "K261D,H265E,Q406E,S446T"="#117733ff", "H265E,Q406E,S446T"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving K261D") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("V337A", "H141V,M154A,K261F,H265A,V337A,Q406E,S446I", "H141V,M154A,K261F,H265A,Q406E,S446I", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("V337A"="#88cceeff", "H141V,M154A,K261F,H265A,V337A,Q406E,S446I"="#117733ff", "H141V,M154A,K261F,H265A,Q406E,S446I"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving V337A") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p
ggsave("../lfcSE_weighted_outputImages/pdf/epistatic-effects-V337A.pdf", plot=p, width=20, height=20)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("Q406E", "H141V,M154A,K261F,H265A,V337A,Q406E,S446I", "H141V,M154A,K261F,H265A,V337A,S446I", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("Q406E"="#88cceeff", "H141V,M154A,K261F,H265A,V337A,Q406E,S446I"="#117733ff", "H141V,M154A,K261F,H265A,V337A,S446I"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving Q406E") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p
ggsave("../lfcSE_weighted_outputImages/pdf/epistatic-effects-Q406E.pdf", plot=p, width=20, height=20)


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("Q406E", "H141V,M154A,Q406E", "H141V,M154A", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("Q406E"="#88cceeff", "H141V,M154A,Q406E"="#117733ff", "H141V,M154A"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving Q406E") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K261A", "K261A,H265K,Q406E,S446A", "H265K,Q406E,S446A", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("K261A"="#88cceeff", "K261A,H265K,Q406E,S446A"="#117733ff", "H265K,Q406E,S446A"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H265A", "M154A,K261A,H265A,Q406D", "M154A,K261A,Q406D", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("H265A"="#88cceeff", "M154A,K261A,H265A,Q406D"="#117733ff", "M154A,K261A,Q406D"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving H265A") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("S446T", "M154A,K261A,Q406D,S446T", "M154A,K261A,Q406D", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("S446T"="#88cceeff", "M154A,K261A,Q406D,S446T"="#117733ff", "M154A,K261A,Q406D"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving S446T") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K261F", "K261F,H265K,Q406D,S446I", "H265K,Q406D,S446I", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("K261F"="#88cceeff", "K261F,H265K,Q406D,S446I"="#117733ff", "H265K,Q406D,S446I"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving K261F") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H265E", "K261D,H265E,Q406E,S446T", "K261D,Q406E,S446T", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("H265E"="#88cceeff", "K261D,H265E,Q406E,S446T"="#117733ff", "K261D,Q406E,S446T"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving H265E") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p


plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("K261A", "K261A,H265K,Q406E,S446A", "H265K,Q406E,S446A", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
different_colors_set <- c("K261A"="#88cceeff", "K261A,H265K,Q406E,S446A"="#117733ff", "H265K,Q406E,S446A"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects involving K261A") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p

7 Variants for which mutant strains exist

“variant H141V,K261A,H265K,Q406E,S446I with the highest fitness value in the library, but shitty adjusted p value, is H141V,K261A,H265K,Q406E,S446I (norm=2.18, p.adj=0.107), alternative option for showing epistasis (besides D and E), needs control strains H141V, K261A, Q406E, S446I and H265K to show that H265K is detrimental when introduced on its own, beneficial in background of quardruple mutant”

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H141V,K261A,H265K,Q406E,S446I", "H141V,K261A,Q406E,S446I", "H265K", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects and 'strain A'")
p

H141V, M154A, K261A, H265A, Q406E, S446T other alternative option for showing epistasis (besides E), there should also be a corresponding mutant with 5 exchanges (H141V, M154A, K261A, Q406E, S446T) and then the comparison that H265A is detrimental on its own, but beneficial in the background of the mutant with 5 exchagnes

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H141V,M154A,K261A,H265A,Q406E,S446T", "H141V,M154A,K261A,Q406E,S446T", "H265A", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects and 'strain D'")
p

H141V, K261E, H265A, S446A “fittest combinatorial mutant variant with a significant difference has four amino acid exchanges: H141V,K261E,H265A,S446A (norm=1.56, p.adj=0.0259) , main line Figure showing epistasis, there should also be a corresponding triple mutant (H141V, K261E, S446A) showing that H265A on its own is detrimental, in background of H141V, K261E, S446A beneficial”

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H141V,K261E,H265A,S446A", "H141V,K261E,S446A", "H265A", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Epistatic effects and 'strain E'")
p

Variant underlying the suggested higher-order variants: H141V, K261A, H265A, Q406E, S446T

different_colors_set <- c("W128I"="#88cceeff", "H141V,K261A,H265A,Q406E,S446T"="#117733ff", "K360I"="#ddcc77ff", "WT"="black", neg_control_K214="darkgray")
plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("H141V,K261A,H265A,Q406E,S446T", "W128I", "K360I", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Variant underlying B, C, Bfix and single exchanges introduced in these variants") + scale_fill_manual(values=different_colors_set) + scale_color_manual(values=different_colors_set)
p
ggsave("../lfcSE_weighted_outputImages/pdf/variant_underlying_B-C-Bfix.pdf", plot=p, width=20, height=20)

W128I single exchange with high fitness value, H358S single exchange with high fitness value, K360I single exchange with high fitness value

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("W128I", "H358S", "K360I", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Single exchanges added cloned as single mutant strains")
p

Higher order variant with V98D, F104C, W128K, Q142D, K233I, V270L, M345I, H358S

plot_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("V98D", "F104C", "W128K", "Q142D", "K233I", "V270L", "M345I", "H358S", "WT", neg_control_K214))
unique(plot_subset[,c("sgRNA_target", "norm", "condition", "num_barcodes")])
p <- lineplot_CVinterval_severalColours_meanlog2(plot_subset) + labs(title="Single exchanges underlying higher order variant ('M8')")
p

8 Other analyses for combinatorial library

single_muts_combinatorial <- pivot_wider(unique(subset(fitness_data, fitness_data$category=="combiANDsatur")[,columns]), names_from=condition, values_from=c(norm, p_fit_adj_WT))
single_muts_combinatorial$norm_multiply <- single_muts_combinatorial$norm_CL_N2 * single_muts_combinatorial$norm_CL_O2 * single_muts_combinatorial$norm_LD
write.csv(single_muts_combinatorial, file="../lfcSE_weighted_outputImages/single_site_exchanges_combinatorialLibrary.csv")
single_muts_combinatorial[order(single_muts_combinatorial$norm_multiply, decreasing=TRUE),c(1,4,5,6,10,2,3,7,8,9)]
combi_single_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("WT", "H265K", "H265R", "V337A", "V337S", neg_control_K214))
p <- lineplot_CVinterval_severalColours_meanlog2(combi_single_subset) + labs(title="Most detrimental single variants in combinatorial library")
p

for(n_mut in 2:7){
  print(n_mut)
  muts_combinatorial <- pivot_wider(unique(subset(fitness_data, fitness_data$number_muts==n_mut)[,columns]), names_from=condition, values_from=c(norm, p_fit_adj_WT))
  muts_combinatorial$norm_multiply <- muts_combinatorial$norm_CL_N2 * muts_combinatorial$norm_CL_O2 * muts_combinatorial$norm_LD
  print("Sorted according to product of different normed fitness values")
  print(head(muts_combinatorial[order(muts_combinatorial$norm_multiply, decreasing=TRUE),c(1,4,5,6,10,2,3,7,8,9)]))
  muts_combinatorial <- subset(muts_combinatorial, muts_combinatorial$norm_CL_N2 > 1.0 & muts_combinatorial$norm_CL_O2 > 1.0 & muts_combinatorial$norm_LD > 1.0)
  muts_combinatorial <- subset(muts_combinatorial, (muts_combinatorial$p_fit_adj_WT_CL_N2 < 0.05 | muts_combinatorial$p_fit_adj_WT_CL_O2 < 0.05 | muts_combinatorial$p_fit_adj_WT_LD < 0.05) & muts_combinatorial$norm_CL_N2 > 1.0 & muts_combinatorial$norm_CL_O2 > 1.0 & muts_combinatorial$norm_LD > 1.0) 
  print("Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions")
  print(head(muts_combinatorial[order(muts_combinatorial$norm_multiply, decreasing=TRUE),c(1,4,5,6,10,2,3,7,8,9)]))
}
[1] 2
[1] "Sorted according to product of different normed fitness values"
[1] "Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions"
[1] 3
[1] "Sorted according to product of different normed fitness values"
[1] "Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions"
[1] 4
[1] "Sorted according to product of different normed fitness values"
[1] "Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions"
[1] 5
[1] "Sorted according to product of different normed fitness values"
[1] "Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions"
[1] 6
[1] "Sorted according to product of different normed fitness values"
[1] "Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions"
[1] 7
[1] "Sorted according to product of different normed fitness values"
[1] "Only selecting for variants with a normed fitness value above 1 in all conditions and a significant difference to the base variant in at least one of the conditions"

9 Truncated variants

truncated_variants <- subset(fitness_data, fitness_data$category =="notExpected")
length(unique(truncated_variants$sgRNA_target))
[1] 34
truncated_wide <- pivot_wider(unique(truncated_variants[,c("sgRNA_target", "norm", "condition", "p_fit_adj_WT", "num_barcodes")]), values_from =c(norm,p_fit_adj_WT), names_from=condition)
truncated_wide$norm_product <- truncated_wide$norm_CL_N2 * truncated_wide$norm_CL_O2 * truncated_wide$norm_LD
truncated_wide[order(truncated_wide$norm_product, decreasing=TRUE),]
truncated_subset <- combi_single_subset <- subset(fitness_data, fitness_data$sgRNA_target %in% c("G395V,STOP395", "F405S,Q406R,N407T,STOP407", "WT", neg_control_K214))
truncated_subset$sgRNA_target <- factor(truncated_subset$sgRNA_target, levels=c("G395V,STOP395", "F405S,Q406R,N407T,STOP407", "WT", neg_control_K214))
different_colors_set <- c("G395V,STOP395"="#44aa99ff", "F405S,Q406R,N407T,STOP407"="#aa4499ff", "WT"="black", "K214R"="darkgray")
different_linetypes_set <- c("G395V,STOP395"=44, "F405S,Q406R,N407T,STOP407"=1343, "WT"="solid", "K214R"="longdash")
p <- lineplot_CVinterval_severalColours_meanlog2(truncated_subset) + scale_linetype_manual(values=different_linetypes_set) + scale_color_manual(values=different_colors_set) + scale_fill_manual(values=different_colors_set)
ggsave("../lfcSE_weighted_outputImages/pdf/truncated_variants_performing_well.pdf", plot=p, height=1.75, width=6, units="in")
p

11 Create files for ProteinNPT

Compare https://github.com/OATML-Markslab/ProteinNPT. Expects a .csv file. “At a minimum this file requires 2 fields: mutated_sequence (full sequence of amino acids) and DMS_score (assay measurement). If no fold variable is included in the assay file, the pipeline script will automatically create a fold_random_5 variable, assigning each mutant to folds 0-4 at random. You may also use your own cross-validation scheme (eg., assign all training sequences to fold 0, all test sequences to fold 1). To that end, you only need to pass to the pipeline script the name of that fold variable via the fold_variable_name argument and specify the index of the test fold via the test_fold_index argument (if test_fold_index is not passed as argument, the script will automatically perform a full cross-validation, rotating the test fold index at each iteration).”

Create a file for a) predicting higher-order combinations - separate amino acid positions into 5 folds –> add folds using python - three files for each condition

Use python script single_muts_train_for_combi.py to assign folds for training etc., output saturational_proteinNPT_with_foldChange.csv

Regarding folds: “We develop 3 distinct cross-validation schemes to assess the ability of each model to extrapolate to positions not encountered during training. In the Random scheme, commonly-used in other supervised fitness prediction benchmarks [Rao et al., 2019, Dallago et al., 2022], each mutation is randomly allocated to one of five distinct folds. In the Contiguous scheme, the sequence is split into five contiguous segments along its length, with mutations assigned to each segment based on the position they occur in the sequence. Lastly, the Modulo scheme uses the modulo operator to assign mutated positions to each fold. For example, position 1 is assigned to fold 1, position 2 to fold 2, and so on, looping back to fold 1 at position 6. This pattern continues throughout the sequence. We note that there is no inherent issue with using a Random cross-validation scheme to estimate the performance of predictive models. However, the conclusions drawn and the generalizability claims based on it require careful consideration.” –> use modulo scheme for assigning folds to saturational library

  1. predicting more combinations
saturational_for_proteinNPT <- unique(subset(fitness_data, fitness_data$number_muts==1)[,c("sgRNA_target", "norm", "baseAA", "condition")])
saturational_for_proteinNPT <- pivot_wider(saturational_for_proteinNPT, names_from=condition, values_from=norm)
write_csv(saturational_for_proteinNPT, "../lfcSE_weighted_outputImages/csv_for_proteinNPT/saturational_for_proteinNPT.csv")

all_for_proteinNPT <- unique(subset(fitness_data, fitness_data$number_muts>1)[,c("sgRNA_target", "norm", "condition")])
all_for_proteinNPT <- pivot_wider(all_for_proteinNPT, names_from=condition, values_from=norm)
write_tsv(all_for_proteinNPT, "../lfcSE_weighted_outputImages/csv_for_proteinNPT/combinatorial_for_proteinNPT.tsv")

12 Create file for Supplement

columns_for_table <- c("sgRNA_target", "num_barcodes", "number_muts", "norm", "condition", "p_fit_adj_WT", "EVcoup_predict", "DeepSeq_predict", "MSA_Transform", "proteinNPT_predict", "additive_score", "conservationScore", "absSAS", "relSAS", "dimer")

write_csv(unique(fitness_data[,columns_for_table]), "../lfcSE_weighted_outputImages/SuppTable_all_fitness_values.csv")

Session Info

LS0tCnRpdGxlOiAiQW5hbHlzaXMgb2YgSWxsdW1pbmEgc2VxZXVuY2luZyBvZiBwb29sZWQgZ3Jvd3RoIG9mIGxhcmdlIENiYk0gbGlicmFyeSIKYXV0aG9yOiAnU1lTVEVNOiBgciB2ZXJzaW9uWzEzXWAnCmRhdGU6ICdEQVRFOiBgciBTeXMudGltZSgpYCcKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICBodG1sX25vdGVib29rOgogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHllcwogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKcGFyYW1zOgogIGNwdXM6IDIKICBpbnB1dF9kaXI6IC4vCiAgbWV0YTogJycKLS0tCgpgYGB7ciBsb2FkLXJlcXVpcmVkLXBhY2thZ2VzLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoREVTZXEyKQpsaWJyYXJ5KERlc2NUb29scykKbGlicmFyeShjb2xvcmJsaW5kcikgI2h0dHBzOi8vZ2l0aHViLmNvbS9jbGF1c3dpbGtlL2NvbG9yYmxpbmRyCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZ2dyZXBlbCkKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ25ld3NjYWxlKQpsaWJyYXJ5KEhlYXRwbHVzKSAjIGJpb2NvbmR1Y3RvcgpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKSAjIGJpb2NvbmR1Y3RvcgpsaWJyYXJ5KGVkZ2VSKQpsaWJyYXJ5KE1mdXp6KQpsaWJyYXJ5KGdnVmVubkRpYWdyYW0pCmxpYnJhcnkocGhlYXRtYXApCmxpYnJhcnkocGx5cikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKYGBge3IgY29sb3ItZGVmaW5pdGlvbnMsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9CmNvbF9jb21iaV9zYXQgPSBjKCJjb21iaW5hdG9yaWFsIj0iIzY5YjNhMiIsICJzYXR1cmF0aW9uYWwiPSIjNDA0MDgwIiwgIldUIj0iYmxhY2siKQpjb2xfY29uZGl0aW9ucyA9IGMoIkNMX04yIj0iI2U2OWYwMGZmIiwgIkNMX08yIj0iIzAyNzJiMmZmIiwgIkxEIj0iIzAwNTYzZTk5IikKYGBgCgojIEJyaWVmIGRlc2NyaXB0aW9uIG9mIGRhdGEgc2V0CgpBIGxpYnJhcnkgb2YgUnViaXNjbyB2YXJpYW50cyBiYXNlZCBvbiBhIG11dGFudCB2YXJpYW50IG9mICpHYWxsaW9uZWxsYSogc3AuIENiYk0gd2FzIGRlc2lnbmVkLiBUaGUgQ2JiTSB2YXJpYW50IGlzIHR3byBhbWlubyBhY2lkIGV4Y2hhbmdlcyBhd2F5IGZyb20gdGhlIHdpbGQtdHlwZSBzZXF1ZW5jZSBhbmQgd2FzIGlkZW50aWZpZWQgYXMgcGFydCBvZiBhIHNtYWxsIGxpYnJhcnkgd2hpY2ggd2FzIHVzZWQgdG8gZXN0YWJsaXNoIHRoZSBzY3JlZW5pbmcgc3lzdGVtLiBGb3IgcmVhc29ucyBvZiBzaW1wbGljaXR5LCBpbiB0aGUgZm9sbG93aW5nLCAiV1QiIHdpbGwgcmVmZXIgdG8gdGhpcyB2YXJpYW50LCB3aGljaCBpcyB0aGUgImJhc2UiIG9mIHRoZSB3aG9sZSBsaWJyYXJ5LgpUaGUgbGFyZ2UgbGlicmFyeSBjb25zaXN0cyBvZiA2NyBwb3NpdGlvbnMsIHdoaWNoIHdlcmUgc2VwYXJhdGVseSBleGNoYW5nZWQgYnkgYW55IG90aGVyIHBvc3NpYmxlIGFtaW5vIGFjaWQgYW5kIGEgY29tYmluYXRvcmlhbCBwYXJ0LCB3aGljaCBpcyBiYXNlZCBvbiBzZXZlbiBhbWlubyBhY2lkIHBvc2l0aW9ucywgd2hpY2ggd2VyZSBleGNoYW5nZWQsIGluIGEgY29tYmluYXRvcmlhbCBmYXNoaW9uLCBieSBhIGZldyBzcGVjaWZpYyBwb3NzaWJsZSByZXNpZHVlcyBwZXIgcG9zaXRpb24uCgpCb3RoIHBhcnRzIG9mIHRoZSBsaWJyYXJ5IHdlcmUgZGVzaWduZWQgd2l0aCB0aGUgaGVscCBvZiBFVm11dGF0aW9uIGFuZCBEZWVwU2VxdWVuY2UsIHdoaWNoIHVzZSBwaHlsb2dlbmV0aWMgaW5mb3JtYXRpb24gdG8gcHJlZGljdCBiZW5lZmljaWFsIGFtaW5vIGFjaWQgZXhjaGFuZ2VzLiBUaGUgZXhhY3QgbWFubmVyIGhvdyBwcmVkaWN0aW9ucyB3ZXJlIHBlcmZvcm1lZCBpcyBkZXNjcmliZWQgYWxvbmcgd2l0aCB0aGUgc21hbGwgbGlicmFyeSB0aGUgImJhc2UiIG11dGFudCB2YXJpYW50IGlzIGRlcml2ZWQgZnJvbS4KClRoZSBjb21wbGV0ZSBsaWJyYXJ5IHdhcyB0cmFuc2Zvcm1lZCBpbnRvIGEgKlN5bmVjaG9jeXN0aXMqIGhvc3Qgc3RyYWluLCBpbiB3aGljaCB0cmFuc2NyaXB0aW9uIG9mIHRoZSBlbmRvZ2Vub3VzIFJ1YmlzY28gdHJhbnNjcmlwdCBjYW4gYmUgaW5oaWJpdGVkIGJ5IHRoZSBpbmR1Y3Rpb24gb2YgYSBDUklTUFIgaW5oaWJpdGlvbiBzeXN0ZW0uIEVhY2ggdmFyaWFudCBpcyByZXByZXNlbnRlZCBieSBvbmUgb3Igc2V2ZXJhbCBjbG9uZXMsIHdoaWNoIGNhbiBiZSBkaXN0aW5ndWlzaGVkIG9uIGJhc2Ugb2YgdGhlaXIgTjIwIGJhcmNvZGVzLiBUaGUgcG9vbGVkIGxpYnJhcnkgb2YgdGhlc2Ugc3RyYWlucyB3YXMgZ3Jvd24gaW4gOCByZXBsaWNhdGVzIGF0IGRpZmZlcmVudCBnYXMgZmVlZCBhbmQgbGlnaHQgY29uZGl0aW9ucyBpbiB0dXJiaWRvc3RhdCBtb2RlIGZvciBhcHByb3hpbWF0ZWx5IDEwIGdlbmVyYXRpb25zIChDTF9OMjogY29uc3RhbnQgbGlnaHQgMzAwIMK1RSwgNSUgQ08yLCA5NSUgTjIsIDAlIE8yLCBDTF9PMjogY29uc3RhbnQgbGlnaHQgMzAwIMK1RSwgNSUgQ08yLCA3NSUgTjIsIDIwJSBPMiwgTEQ6IGxpZ2h0LWRhcmsgY3ljbGVzLCA1JSBDTzIsIDc1JSBOMiwgMjAlIE8yKS4gQnkgdGFraW5nIHNhbXBsZXMgZm9yIElsbHVtaW5hIHNlcXVlbmNpbmcgcmVndWxhcmx5LCB3ZSB3ZXJlIGFibGUgdG8gdHJhY2sgYWxtb3N0IGFsbCB2YXJpYW50cyBwcmVzZW50IGFuZCBhc3NpZ24gZml0bmVzcyB2YWx1ZXMuCgpOb3RlIHRoYXQgYW1pbm8gYWNpZCBudW1iZXJpbmcgaW4gdGhlIGZvbGxvd2luZyBhcmUgYmFzZWQgb24gdGhlIHNlcXVlbmNlIGluY2x1ZGluZyB0aGUgTi1TdHJlcCB0YWcuCgpGaXRuZXNzIHZhbHVlcyBhcmUgZ2l2ZW4gYXMgbm9ybWFsaXplZCB2YWx1ZXMsIGkuZS4gYSB2YWx1ZSBvZiAxLjAgZXF1YWxzIHRoZSBmaXRuZXNzIG9mIHRoZSBiYXNlIHZhcmlhbnQgKGFjdHVhbGx5IHRoZSB1bmRlcmx5aW5nIENiYk0gdmFyaWFudCB3aGljaCBpcyB0d28gYW1pbm8gYWNpZCBleGNoYW5nZXMgYXdheSBmcm9tIHdpbGQtdHlwZSBDYmJNKSBhbmQgMC4wIGVxdWFscyB0aGUgbWVkaWFuIHZhbHVlIG9mIEsyMTQgdmFyaWFudHMgcHJlc2VudCBpbiB0aGUgZGF0YSBzZXQuIEsyMTQgaXMgdGhlIGNhcmJhbXlsYXRlZCBseXNpbmUgd2hpY2ggaXMgZXNzZW50aWFsIGZvciBjYXRhbHlzaXMuIEV4Y2hhbmdlcyBvZiB0aGlzIGx5c2luZSByZXNpZHVlIHNob3VsZCByZXN1bHQgaW4gY2F0YWx5dGljYWxseSBpbmFjdGl2ZSBlbnp5bWUuIE11dGFudCB2YXJpYW50cyB3aXRoIHdvcnNlIGZpdG5lc3MgdmFsdWVzIHRoYW4gSzIxNCBzdWJzdGl0dXRpb25zIGNhbiBiZSBjb25zaWRlcmVkIGNhdGFseXRpY2FsbHkgaW5hY3RpdmUuCgpJbiBhIGZpcnN0IHN0ZXAsIGFsbCBkYXRhIG9mIHJlbGV2YW5jZSB3aWxsIGJlIGxvYWRlZC4KCmBgYHtyIGxvYWQtcmVxdWlyZWQtZGF0YXNldHMsIG1lc3NhZ2U9RkFMU0UsIGVjaG89RkFMU0V9CmNvdW50X21hdHJpeCA8LSByZWFkX3RzdigiLi4vcmVzdWx0cy9wcmVwYXJlL2FsbF9jb3VudHMudHN2IikKY291bnRfbWF0cml4JEdlbmUgPC0gTlVMTApmaXRuZXNzX2RhdGEgPC0gIHJlYWRfdHN2KCIuLi9XZWlnaHRpbmdTdHJhdGVnaWVzX2ZpdG5lc3NDYWxjL3Jlc3VsdF9sZmNTRV9QZWFyc29uX1dlaWdodGVkX3NhbWVXZWlnaHQudHN2IikKc2F0dV9jb21iaSA8LSByZWFkX3RzdigiLi4vaW5wdXQvY29tYmlfc2F0dXJfbXV0YXRpb3MudHN2IiwgY29sX25hbWVzID0gYygic2dSTkFfdGFyZ2V0IiwgImNhdGVnb3J5IikpCmZpdG5lc3NfZGF0YSA8LSBsZWZ0X2pvaW4oZml0bmVzc19kYXRhLCBzYXR1X2NvbWJpKQpudW1iZXJfbXV0cyA8LSByZWFkX3RzdigiLi4vaW5wdXQvbnVtYmVyX211dGF0aW9uc192YXJpYW50cy50c3YiKQpmaXRuZXNzX2RhdGEgPC0gbGVmdF9qb2luKGZpdG5lc3NfZGF0YSwgbnVtYmVyX211dHMpCgpkZl9zYW1wbGVzaGVldCA8LSByZWFkcjo6cmVhZF9jc3YoIi4uL2lucHV0L3NhbXBsZXNoZWV0XzFzdEN1bHRpdl8ybmRDdWx0aXYuY3N2IiwgY29sX3R5cGVzID0gY29scygpKSAlPiUKICAgIHNlbGVjdChhbGxfb2YoYygic2FtcGxlIiwgImNvbmRpdGlvbiIsICJyZXBsaWNhdGUiLCAidGltZSIsICJncm91cCIsICJyZWZlcmVuY2VfZ3JvdXAiKSkpICU+JQogICAgZHBseXI6Om11dGF0ZShncm91cCA9IGZhY3RvcihgZ3JvdXBgKSkKZGZfc2FtcGxlc2hlZXQkbmFtZSA8LSBwYXN0ZSgiZ2VuLCIsIGRmX3NhbXBsZXNoZWV0JHRpbWUsICIsciwiLCBkZl9zYW1wbGVzaGVldCRyZXBsaWNhdGUsICIsY29uZCwiLCBkZl9zYW1wbGVzaGVldCRjb25kaXRpb24sIHNlcD0iIikKCnR1bm5lbF9tdXRhdGlvbnMgPC0gcmVhZF90c3YoIi4uL2lucHV0L2xpc3RfdHVubmVsX211dGF0aW9uc193b092ZXJsYXBEZWVwU2VxdWVuY2UudHh0IiwgY29sX25hbWVzID0gYygibm9OU3RyZXAiLCAiYWFfcG9zX3dpdGhTdHJlcCIpKQpmaXRuZXNzX2RhdGEkYmFzZUFBIDwtIHN1YnN0cihmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0LCAxLCBuY2hhcihmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0KS0xKQpmaXRuZXNzX2RhdGEgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgIWZpdG5lc3NfZGF0YSRiYXNlQUEgJWluJSB0dW5uZWxfbXV0YXRpb25zJGFhX3Bvc193aXRoU3RyZXApCgpFVmNvdXBsaW5nc19wcmVkaWN0IDwtIHJlYWRfdHN2KCIuLi9pbnNpbGljb19wcmVkaWN0aW9ucy9FVmNvdXBsaW5ncy92YXJpYW50c19zY29yZWRfRVZjb3VwbGluZ3MudHN2IikKbmFtZXMoRVZjb3VwbGluZ3NfcHJlZGljdCkgPC0gYygic2dSTkFfdGFyZ2V0IiwgIkVWY291cF9wcmVkaWN0IikKZml0bmVzc19kYXRhIDwtIGxlZnRfam9pbihmaXRuZXNzX2RhdGEsIEVWY291cGxpbmdzX3ByZWRpY3QpCgojRGVlcFNlcV9wcmVkaWN0X3NpbmdsZSA8LSByZWFkX2RlbGltKCIuLi9pbnNpbGljb19wcmVkaWN0aW9ucy9EZWVwU2VxdWVuY2UvMjAyMy0wNC0yMF9zaW5nbGVfbXV0YW50X21hdHJpeF9HYWxsaW9uZWxsYV9SdWJpc2NvX0lfc2FtcGxlcy0xMF9lbGJvX3ByZWRpY3Rpb25zLmNzdiIsIGNvbF9uYW1lcyA9IGMoInNnUk5BX3RhcmdldCIsICJEZWVwU2VxX3ByZWRpY3Rfc2luZ2xlIiksIGRlbGltPSI7IikKI2ZpdG5lc3NfZGF0YSA8LSBsZWZ0X2pvaW4oZml0bmVzc19kYXRhLCBEZWVwU2VxX3ByZWRpY3QpCgpEZWVwU2VxX3ByZWRpY3RfY29tcGxldGUgPC0gcmVhZF90c3YoIi4uL2luc2lsaWNvX3ByZWRpY3Rpb25zL0RlZXBTZXF1ZW5jZS8yMDI0LTA3LTMwX3ZhcmlhbnRzX3Njb3JlZF9EZWVwU2VxdWVuY2UudHN2IiwgY29sX25hbWVzID0gYygic2dSTkFfdGFyZ2V0IiwgIkRlZXBTZXFfcHJlZGljdCIpKQpmaXRuZXNzX2RhdGEgPC0gbGVmdF9qb2luKGZpdG5lc3NfZGF0YSwgRGVlcFNlcV9wcmVkaWN0X2NvbXBsZXRlKQoKIyBydW4gYWRkaXRpdmVNb2RlbC5weSBpZiByZS1hbmFseXNpbmcKYWRkaXRpdmVfc2NvcmVzX0NMX04yIDwtIHJlYWRfdHN2KCIuLi9pbnNpbGljb19wcmVkaWN0aW9ucy9hZGRpdGl2ZU1vZGVsL3Jlc3VsdHMvY29tYmluYXRvcmlhbF92YXJpYW50c19hZGRpdGl2ZVNjb3Jlc19DTF9OMi5jc3YiKVssYygyLDQpXQphZGRpdGl2ZV9zY29yZXNfQ0xfTjIkY29uZGl0aW9uIDwtICJDTF9OMiIKYWRkaXRpdmVfc2NvcmVzX0NMX08yIDwtIHJlYWRfdHN2KCIuLi9pbnNpbGljb19wcmVkaWN0aW9ucy9hZGRpdGl2ZU1vZGVsL3Jlc3VsdHMvY29tYmluYXRvcmlhbF92YXJpYW50c19hZGRpdGl2ZVNjb3Jlc19DTF9PMi5jc3YiKVssYygyLDQpXQphZGRpdGl2ZV9zY29yZXNfQ0xfTzIkY29uZGl0aW9uIDwtICJDTF9PMiIKYWRkaXRpdmVfc2NvcmVzX0xEIDwtIHJlYWRfdHN2KCIuLi9pbnNpbGljb19wcmVkaWN0aW9ucy9hZGRpdGl2ZU1vZGVsL3Jlc3VsdHMvY29tYmluYXRvcmlhbF92YXJpYW50c19hZGRpdGl2ZVNjb3Jlc19MRC5jc3YiKVssYygyLDQpXQphZGRpdGl2ZV9zY29yZXNfTEQkY29uZGl0aW9uIDwtICJMRCIKYWRkaXRpdmVfc2NvcmVzIDwtIGJpbmRfcm93cyhhZGRpdGl2ZV9zY29yZXNfQ0xfTjIsIGFkZGl0aXZlX3Njb3Jlc19DTF9PMiwgYWRkaXRpdmVfc2NvcmVzX0xEKQpmaXRuZXNzX2RhdGEgPC0gbGVmdF9qb2luKGZpdG5lc3NfZGF0YSwgYWRkaXRpdmVfc2NvcmVzKQoKY29uc2VydmF0aW9uX3Njb3JlcyA8LSByZWFkX2NzdigiLi4vaW5wdXQvRVZjb3VwbGluZ3NfRGVlcFNlcXUvVEFSR0VUX2IwLjNfc2luZ2xlX211dGFudF9tYXRyaXguY3N2IilbLGMoMiw3KV0KbmFtZXMoY29uc2VydmF0aW9uX3Njb3JlcykgPC0gYygic2dSTkFfdGFyZ2V0IiwgImNvbnNlcnZhdGlvblNjb3JlIikKZml0bmVzc19kYXRhIDwtIGxlZnRfam9pbihmaXRuZXNzX2RhdGEsIGNvbnNlcnZhdGlvbl9zY29yZXMpCgpyZWxTQVNfc2NvcmVzIDwtIHJlYWRfdHN2KCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvc3ZnL0ZpZzEvU3RydWN0dXJlX1B5TW9sL3JlbFNBU19HYWxsaW9uZWxsYV9DYmJNLUkudHN2IikKZml0bmVzc19kYXRhIDwtIGxlZnRfam9pbihmaXRuZXNzX2RhdGEsIHJlbFNBU19zY29yZXMpCgpkaW1lciA8LSByZWFkX3RzdigiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3N2Zy9GaWcxL1N0cnVjdHVyZV9QeU1vbC9kaW1lcl9hYS50c3YiKQpmaXRuZXNzX2RhdGEgPC0gbGVmdF9qb2luKGZpdG5lc3NfZGF0YSwgZGltZXIpCgojIGdldCBNU0EgVHJhbnNmb3JtZXIgZW5zZW1ibGUgcHJlZGljdGlvbnMgZnJvbSBQcm90ZWluTlBULCBydW4gY2hhbmdlX211dGFudF9uYW1lX0NCQk1fVFJBSU4ucHkgb24gZmlsZSB0byBnZXQgb25seSBwcmVkaWN0aW9ucyBhbmQgbXV0YW50IG5hbWUsIGxvYWQgYW5kIG1lcmdlCk1TQXRyYW5zZiA8LSByZWFkX3RzdigiLi4vaW5wdXQvQ0JCTV9UUkFJTl9vbmx5X3ByZWRpY3RBbmRNdXRhbnQudHN2IikKZml0bmVzc19kYXRhIDwtIGxlZnRfam9pbihmaXRuZXNzX2RhdGEsIE1TQXRyYW5zZikKCnByb3RlaW5OUFQgPC0gcmVhZF90c3YoIi4uL2lucHV0L1Byb3RlaW5OUFRfbXV0YW50X2ZpdG5lc3NQcmVkaWN0aW9ucy50c3YiKQpwcm90ZWluTlBUIDwtIHBpdm90X2xvbmdlcihwcm90ZWluTlBULCBjb2xzPWMoMiwzLDQpLCBuYW1lc190bz0iY29uZGl0aW9uIiwgdmFsdWVzX3RvPSJwcm90ZWluTlBUX3ByZWRpY3QiKQpmaXRuZXNzX2RhdGEgPC0gbGVmdF9qb2luKGZpdG5lc3NfZGF0YSwgcHJvdGVpbk5QVCkKYGBgCgojIERpYWdub3N0aWMgcGxvdHMgZm9yIGN1bHRpdmF0aW9uCgojIyBTYW1wbGUtc2FtcGxlIGNvcnJlbGF0aW9uCgpXaGVuIGNsdXN0ZXJpbmcgYWNjb3JkaW5nIHRvIHNpbWlsYXJpdHksIHJlcGxpY2F0ZXMgZnJvbSB0aGUgc2FtZSBnYXMgY29uZGl0aW9ucyBoYXZlIGEgdGVuZGVuY3kgdG8gY2x1c3RlciB0b2dldGhlciwgaXJyZXNwZWN0aXZlIG9mIHRoZWlyIGxpZ2h0IGNvbmRpdGlvbiAoY29uc3RhbnQgbGlnaHQgb3IgbGlnaHQtZGFyaykuIFRoaXMgaXMgYWxzbyB0aGUgY2FzZSBhdCBnZW5lcmF0aW9uIDAgLSBldmVuIGF0IGdlbmVyYXRpb24gMCwgc2FtcGxlcyBhcmUgbW9yZSBzaW1pbGFyIHRvIG90aGVyIHNhbXBsZXMgZnJvbSB0aGUgc2FtZSBnYXMgZmVlZCB0aGFuIHRvIGdlbmVyYXRpb24gMCBzYW1wbGVzIG9mIHRoZSBvdGhlciByb3VuZCBvZiBjdWx0aXZhdGlvbiAoZmlyc3Qgcm91bmQgb2YgY3VsdGl2YXRpb24gd2FzIENMX04yOyBDTF9PMiBhbmQgTEQgd2VyZSBydW4gaW4gYSBzZWNvbmQgcm91bmQgb2YgY3VsdGl2YXRpb25zKS4KClRoZXJlIGlzIHNvbWUgY2xlYXIgc2VwYXJhdGlvbiBiZXR3ZWVuIHNhbXBsZXMgZnJvbSBlYXJsaWVyIHRpbWUgcG9pbnRzIGFuZCBzYW1wbGVzIGZyb20gbGF0ZXIgdGltZSBwb2ludHMuCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMCwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlPUZBTFNFfQpkZl9jb3VudHMgPC0gdGlkeXI6OnBpdm90X2xvbmdlcihjb3VudF9tYXRyaXgsCiAgICBjb2xzID0gMjpuY29sKGNvdW50X21hdHJpeCksCiAgICBuYW1lc190byA9ICJzYW1wbGUiLCB2YWx1ZXNfdG8gPSAibl9yZWFkcyIKKQoKIyBzb3J0CmRmX2NvdW50cyA8LSBhcnJhbmdlKGRmX2NvdW50cywgc2FtcGxlKQpkZl9jb3VudHMgPC0gbGVmdF9qb2luKGRmX3NhbXBsZXNoZWV0LCBkZl9jb3VudHMpCgpkZl9jb3JyZWxhdGlvbiA8LSBkZl9jb3VudHNbLGMoIm5hbWUiLCAic2dSTkEiLCAibl9yZWFkcyIpXSAlPiUKICAgIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gIm5hbWUiLCB2YWx1ZXNfZnJvbSA9ICJuX3JlYWRzIikgJT4lCiAgICBkcGx5cjo6c2VsZWN0KC1jKDEpKSAlPiUKICAgIGNvcigpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTAsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZT1GQUxTRX0KYW5ub3RhdGlvbl9kYXlzID0gZGF0YS5mcmFtZShyb3cubmFtZXM9dW5pcXVlKHJvdy5uYW1lcyhkZl9jb3JyZWxhdGlvbikpLCBnZW5lcmF0aW9uPWFzLmNoYXJhY3RlcihjKHJlcChjKCIwIiwgIjMuNyIsICI4LjYiLCAiMTAuMSIpLCAyKSwgcmVwKGMoIjEwLjEiLCAiOC42IiwgIjMuNyIsICIwIiksIDIpLCByZXAoYygiMy43IiwgIjEwLjEiLCAiMCIsICI4LjYiKSwgMiksICI4LjYiLCAiMCIsICIxMC4xIiwgIjMuNyIsICIzLjciLCAiMCIsICIwIiwgIjUuNCIsICI2LjQiLCAiNy43IiwgIjEwLjMiLCAiMCIsICI1LjQiLCAiMCIsICI1LjQiLCByZXAoYygiMCIsICI1LjQiLCAiNi40IiwgIjcuNyIsICIxMC4zIiksIDUpLCByZXAoYygiMCIsICI0LjkiLCAiOC42IiwgIjEyLjUiKSw4KSkpLCBjb25kaXRpb249YyhyZXAoIkNMX04yIiwgMzApLCByZXAoIkxEIiwgMzQpLCByZXAoIkNMX08yIiwgMzIpKSwgcmVwbGljYXRlPWFzLmNoYXJhY3RlcihjKHJlcCgiMSIsIDQpLCByZXAoIjIiLCA0KSwgcmVwKCIzIiwgNCksIHJlcCgiNCIsIDQpLCByZXAoIjUiLCA0KSwgcmVwKCI2IiwgNCksIHJlcCgiNyIsIDQpLCByZXAoIjgiLCAyKSwgcmVwKCIxIiwgNSksIHJlcCgiMiIsIDIpLCByZXAoIjMiLCAyKSwgcmVwKCI0IiwgNSksIHJlcCgiNSIsIDUpLCByZXAoIjYiLCA1KSwgcmVwKCI3IiwgNSksIHJlcCgiOCIsIDUpLCByZXAoIjEiLCA0KSwgcmVwKCIyIiwgNCksIHJlcCgiMyIsIDQpLCByZXAoIjQiLCA0KSwgcmVwKCI1IiwgNCksIHJlcCgiNiIsIDQpLCByZXAoIjciLCA0KSwgcmVwKCI4IiwgNCkpKSwgZ2FzX2ZlZWQ9YyhyZXAoIk4yIiwgMzApLCByZXAoIk8yIiwgNjYpKSkKCiMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNDE2Mjg0NTAvci1waGVhdG1hcC1jaGFuZ2UtYW5ub3RhdGlvbi1jb2xvcnMtYW5kLXByZXZlbnQtZ3JhcGhpY3Mtd2luZG93LWZyb20tcG9wcGluZy11cAojIGNob29zZSBncmFkaWVudCBvZiBjb2xvcnMgZm9yIGdlbmVyYXRpb25zCiMgZS5nLiBUb2wgZnJvbSBodHRwczovL2RhdmlkbWF0aGxvZ2ljLmNvbS9jb2xvcmJsaW5kLyMlMjNEODFCNjAtJTIzMUU4OEU1LSUyM0ZGQzEwNy0lMjMwMDRENDAKb2thYmUgPC0gYygiI2YwZTQ0MmZmIiwgIiNlNjlmMDBmZiIsICIjZDU1ZTAwZmYiLCAiI2NjNzlhN2ZmIiwgIiMwMDllNzNmZiIsICIjNTZiNGU5ZmYiLCAiIzAwNzJiMmZmIiwgIiNhYWFhYWFmZiIpCmdlbmVyYXRpb25zIDwtIG9rYWJlW2MoMSwgMiwgMywgMywgNCwgNCwgNCwgNSwgNSwgNSldCiMgZ2VuZXJhdGlvbnMgIjAiICAgICIzLjciICAiOC42IiAgIjEwLjEiICI1LjQiICAiNi40IiAgIjcuNyIgICIxMC4zIiAiNC45IiAgIjEyLjUiCm5hbWVzKGdlbmVyYXRpb25zKSA8LSBjKCIwIiwgIjMuNyIsICI0LjkiLCI1LjQiLCAiNi40IiwgIjcuNyIsICI4LjYiLCAiMTAuMSIsICIxMC4zIiwgIjEyLjUiKQojIHJlcGxpY2F0ZXMgMSB0byA4CnJlcCA8LSBva2FiZVsxOjhdCm5hbWVzKHJlcCkgPC0gYXMuY2hhcmFjdGVyKDE6OCkKZmVlZCA8LSBva2FiZVtjKDIsIDcpXQpuYW1lcyhmZWVkKSA8LSBjKCJOMiIsICJPMiIpCmFubm90YXRpb25fY29sb3JfbGlzdCA8LSBsaXN0KGNvbmRpdGlvbj1jb2xfY29uZGl0aW9ucywgcmVwbGljYXRlPXJlcCwgZ2VuZXJhdGlvbj1nZW5lcmF0aW9ucywgZ2FzX2ZlZWQ9ZmVlZCkKCiMgaHR0cHM6Ly9iaW9pbmZvcm1hdGljcy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvMjI1MDIvbWFudWFsbHktc2V0LXJhbmdlLW9mLWNvbG91ci1zY2FsZS1pbi1waGVhdG1hcC1pbi1yCmNvbG9yLmRpdmlzaW9ucyA8LSAxMDAKCnAgPC0gcGhlYXRtYXAoZGZfY29ycmVsYXRpb24sIGRpc3BsYXlfbnVtYmVycz1UUlVFLCB0cmVlaGVpZ2h0X2NvbD0wLCBjdXRyZWVfcm93cyA9IDYsIGN1dHJlZV9jb2xzID0gNiwgYW5ub3RhdGlvbl9yb3cgPSBhbm5vdGF0aW9uX2RheXMsIGFubm90YXRpb25fY29sb3JzID0gYW5ub3RhdGlvbl9jb2xvcl9saXN0LCBicmVha3MgPSBzZXEoMCwxLCBsZW5ndGgub3V0PShjb2xvci5kaXZpc2lvbnMgKyAxKSksIGZvbnRzaXplPTYpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9jb3JyZWxhdGlvbl9zYW1wbGVzX2NsdXN0ZXJpbmdfbnVtYmVycy5wZGYiLCBwbG90PXAsIHdpZHRoPTIwLCBoZWlnaHQ9MjApCgpwIDwtIHBoZWF0bWFwKGRmX2NvcnJlbGF0aW9uLCBkaXNwbGF5X251bWJlcnM9RkFMU0UsIHRyZWVoZWlnaHRfY29sPTAsIGN1dHJlZV9yb3dzID0gNiwgY3V0cmVlX2NvbHMgPSA2LCBhbm5vdGF0aW9uX3JvdyA9IGFubm90YXRpb25fZGF5cywgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdGF0aW9uX2NvbG9yX2xpc3QsIGJyZWFrcyA9IHNlcSgwLDEsIGxlbmd0aC5vdXQ9KGNvbG9yLmRpdmlzaW9ucyArIDEpKSwgZm9udHNpemU9NikKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvY29ycmVsYXRpb25fc2FtcGxlc19jbHVzdGVyaW5nLnBuZyIsIHBsb3Q9cCwgd2lkdGg9MTEuNSwgaGVpZ2h0PTEwKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvY29ycmVsYXRpb25fc2FtcGxlc19jbHVzdGVyaW5nLnBkZiIsIHBsb3Q9cCwgd2lkdGg9MTEuNSwgaGVpZ2h0PTEwKQpgYGAKCiMjIENoZWNrIGVxdWFsIGRpc3RyaWJ1dGlvbiBpbiB0aW1lIDAgc2FtcGxlcwoKRm9yIGNoZWNraW5nIHRoZSBkaXN0cmlidXRpb24gYXQgdGltZSBwb2ludCAwLCB0d28gdCA9IDAgcmVwbGljYXRlcyBmcm9tIGVhY2ggY29uZGl0aW9uIHdlcmUgdXNlZC4gCgpgYGB7ciBwcmVwYXJlLWNvdW50LW1hdHJpeC1ERVNlcTJ9CmNvdW50X21hdHJpeF90aW1lMCA8LSBhcy5kYXRhLmZyYW1lKGRhdGEuZnJhbWUoc2dSTkE9Y291bnRfbWF0cml4JHNnUk5BLCBMRF8xPWNvdW50X21hdHJpeCRMRDFBMSwgTERfMj1jb3VudF9tYXRyaXgkTEQxQTYsIE8yXzE9Y291bnRfbWF0cml4JE8yMkExLCBPMl8yPWNvdW50X21hdHJpeCRPMjJBNSwgTjJfMT1jb3VudF9tYXRyaXgkaGlnaE40QTEsIE4yXzI9Y291bnRfbWF0cml4JGhpZ2hONEE1KSkKCnJvdy5uYW1lcyhjb3VudF9tYXRyaXhfdGltZTApIDwtIGNvdW50X21hdHJpeF90aW1lMCRzZ1JOQQpjb3VudF9tYXRyaXhfdGltZTAkc2dSTkEgPC0gTlVMTAoKZGVzaWduX21hdHJpeCA8LSBkYXRhLmZyYW1lKGdyb3VwPXJlcCgidDAiLCA2KSkKcm93Lm5hbWVzKGRlc2lnbl9tYXRyaXgpID0gbmFtZXMoY291bnRfbWF0cml4X3RpbWUwKQpgYGAKCkRFU2VxMiBpcyB1c2VkIHRvIG5vcm1hbGl6ZSBhbW9uZyB0aGUgcmVwbGljYXRlcy4KCmBgYHtyIG5vcm1hbGl6ZS11c2luZy1ERVNlcTIsIG1lc3NhZ2U9RkFMU0V9CmRkc3RpbWUwIDwtIERFU2VxRGF0YVNldEZyb21NYXRyaXgoCiAgY291bnREYXRhID0gY291bnRfbWF0cml4X3RpbWUwLAogIGNvbERhdGEgPSBkZXNpZ25fbWF0cml4LAogIGRlc2lnbiA9IH4gMSkKZGRzdGltZTAgPC0gZXN0aW1hdGVTaXplRmFjdG9ycyhkZHN0aW1lMCkKY291bnRzX25vcm1fZGRzdGltZTAgPC0gYXMuZGF0YS5mcmFtZShjb3VudHMoZGRzdGltZTAsIG5vcm1hbGl6ZWQ9VFJVRSkpCmBgYAoKV2hlbiBsb2cxMC10cmFuc2Zvcm1pbmcgdGhlIHgtc2NhbGUgb2YgdGhlIG1lYW4gb2YgY291bnRzIGluIHRoZSBkaWZmZXJlbnQgc2FtcGxlcywgdGhlIGRpc3RyaWJ1dGlvbiBsb29rcyBhcyBpZiBtZWFuIHZhbHVlcyB3ZXJlIGFsbW9zdCBub3JtYWxseSBkaXN0cmlidXRlZC4gVGhpcyBpcywgaWYgSSBhbSBub3QgbWlzdGFrZW4sIHJlbGF0aXZlbHkgdHlwaWNhbCBmb3IgY291bnQgZGF0YS4gKENvdW50IGRhdGEgZG9lcyB1c3VhbGx5IGZvbGxvdyBhIFBvaXNzb24gZGlzdHJpYnV0aW9uLCB3aGljaCBsb29rcyBtb3JlICJub3JtYWwiIHdoZW4gYmVpbmcgbG9nLXRyYW5zZm9ybWVkKQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CmRmX3Bsb3QgPC0gZGF0YS5mcmFtZShtdXQ9cm93Lm5hbWVzKGNvdW50c19ub3JtX2Rkc3RpbWUwKSwgbWVhbl9jb3VudD1hcHBseShjb3VudHNfbm9ybV9kZHN0aW1lMCwgMSwgbWVhbikpCnAgPC0gZ2dwbG90KGRmX3Bsb3QsIGFlcyh4PW1lYW5fY291bnQpKSArIGdlb21faGlzdG9ncmFtKCkgKyB0aGVtZV9saWdodCgpICsgc2NhbGVfeF9sb2cxMCgpCnAKYGBgCgpUaGUgcGxvdCBiZWxvdyBzaG93cyBtZWFuIGFuZCBzdGRldiBvZiB0aGUgY291bnQgb2YgMTAwIHJhbmRvbWx5IHBpY2tlZCBiYXJjb2RlcyBwcmVzZW50IGluIHRoZSBzYW1wbGVzIGF0IHRpbWUgcG9pbnQgMCBhbmQgY29uZmlybXMgdGhlIGhpc3RvZ3JhbSBmcm9tIGFib3ZlLiBUaGVyZSBhcmUgb3V0bGllcnMgd2l0aCBtYW55IGNvdW50cyBvciBhbG1vc3Qgbm8gY291bnRzIGFuZCBhIGh1Z2UgYnVsayBvZiBiYXJjb2RlcyB3aXRoIGEgc2ltaWxhciBjb3VudC4gQWNjb3JkaW5nIHRvIHRoZSBoaXN0b2dyYW0gYWJvdmUsIHRoaXMgYnVsayBpcyBhcHByb3guIGF0IGEgbWVhbiBjb3VudCBvZiAxMC4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmRmX3Bsb3QgPC0gY291bnRzX25vcm1fZGRzdGltZTBbcm91bmQocnVuaWYobj0xMDAsIG1pbj0xLCBtYXg9bnJvdyhjb3VudHNfbm9ybV9kZHN0aW1lMCkpKSxdCmRmX3Bsb3QgPC0gZGF0YS5mcmFtZShtdXQ9cm93Lm5hbWVzKGRmX3Bsb3QpLCBtZWFuX2NvdW50PWFwcGx5KGRmX3Bsb3QsIDEsIG1lYW4pLCBzdGRldj1hcHBseShkZl9wbG90LDEsc2QpKQpwIDwtIGdncGxvdChkZl9wbG90KSArIGdlb21fYmFyKGFlcyh4PXJlb3JkZXIobXV0LCBtZWFuX2NvdW50KSwgeT1tZWFuX2NvdW50KSwgc3RhdD0iaWRlbnRpdHkiKSArIGdlb21fZXJyb3JiYXIoYWVzKHg9bXV0LCB5bWluPW1lYW5fY291bnQtc3RkZXYsIHltYXg9bWVhbl9jb3VudCtzdGRldiksIHdpZHRoPTAuNCkgKyB0aGVtZV9saWdodCgpICsgeGxhYigiMTAwIHJhbmRvbWx5IHBpY2tlZCBiYXJjb2RlcyIpICsgeWxhYigiTWVhbiBvZiBub3JtYWxpemVkIHJlYWQgY291bnRzIikgKyBsYWJzKHRpdGxlPSJSYW5kb21seSBwaWNrZWQgYmFyY29kZXMgYW5kIHRoZWlyIGF2ZXJhZ2UgYWJ1bmRhbmNlIikgKyB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3RpbWUwX2Rpc3RyaWJ1dGlvbi5wZGYiLCBwbG90PXApCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy90aW1lMF9kaXN0cmlidXRpb24ucG5nIiwgcGxvdD1wKQpgYGAKCkNhbGN1bGF0ZSB0aGUgR2luaSBpbmRleCBvZiBhIGZldyBvZiB0aGUgc2FtcGxlcywgYSB2YWx1ZSBvZiAwIHJlZmxlY3RzIGNvbXBsZXRlIGVxdWFsaXR5LCBhIHZhbHVlIG9mIDEgbWF4aW1hbCBpbmVxdWFsaXR5LiBHaW5pIGluZGljZXMgZm9yIGFsbCBzYW1wbGVzIGFyZSBhbHNvIGNhbGN1bGF0ZWQgYXMgcGFydCBvZiB0aGUgd2hvbGUgbmYtY3Jpc3ByaXNjcmVlbiBwaXBlbGluZS4gV2l0aCB2YWx1ZXMgb2YgYXBwcm94LiAwLjU1IHRvIDAuNiwgYmFyY29kZXMgYXQgdGltZSBwb2ludCAwIGFyZSBub3QgY29tcGxldGVseSBpbmVxdWFsbHkgZGlzdHJpYnV0ZWQsIGJ1dCBhbHNvIGZhciBmcm9tIHBlcmZlY3QgZXF1YWxpdHkuCgpgYGB7ciBHaW5pLWluZGV4fQpHaW5pKGNvdW50c19ub3JtX2Rkc3RpbWUwJExEXzEsIHVuYmlhc2VkPUZBTFNFKQpHaW5pKGNvdW50c19ub3JtX2Rkc3RpbWUwJExEXzIsIHVuYmlhc2VkPUZBTFNFKQpHaW5pKGNvdW50c19ub3JtX2Rkc3RpbWUwJE8yXzEsIHVuYmlhc2VkPUZBTFNFKQpHaW5pKGNvdW50c19ub3JtX2Rkc3RpbWUwJE8yXzIsIHVuYmlhc2VkPUZBTFNFKQpHaW5pKGNvdW50c19ub3JtX2Rkc3RpbWUwJE4yXzEsIHVuYmlhc2VkPUZBTFNFKQpHaW5pKGNvdW50c19ub3JtX2Rkc3RpbWUwJE4yXzIsIHVuYmlhc2VkPUZBTFNFKQpHaW5pKGRmX3Bsb3QkbWVhbl9jb3VudCwgdW5iaWFzZWQ9RkFMU0UpCmBgYAoKIyBFeHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzCgojIyBPdmVydmlldyBmaXRuZXNzIGRpc3RyaWJ1dGlvbgoKTm9ybWFsaXphdGlvbjogV1QgaXMgc2V0IHRvICIxIiwgYW5kIHRoZSBtZWRpYW4gb2YgSzIxNCBzdWJzdGl0dXRpb25zIGFzICIwIi4gRm9yIGFsbCB0aHJlZSBjb25kaXRpb25zLCB0aGUgZGF0YSBzZXQgc2hvd3MgYSBiaW1vZGFsIGRpc3RyaWJ1dGlvbi4gVGhpcyBpcyBtb3N0IHByb25vdW5jZWQgZm9yIHRoZSBjb250aW51b3VzIGxpZ2h0LCBOMiBmZWVkIGNvbmRpdGlvbi4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CnBsb3Rfc3Vic2V0IDwtIHVuaXF1ZShmaXRuZXNzX2RhdGFbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgInBfZml0X2Fkal9XVCIsICJjb25kaXRpb24iKV0pCgpwIDwtIGdncGxvdChwbG90X3N1YnNldCwgYWVzKHg9bm9ybSwgZ3JvdXA9Y29uZGl0aW9uLCBmaWxsPWNvbmRpdGlvbiwgY29sb3I9Y29uZGl0aW9uLCBsaW5ldHlwZT1jb25kaXRpb24pKSArIGdlb21fZGVuc2l0eShhZGp1c3Q9MS41LCBhbHBoYT0uNCkgKyB0aGVtZV9saWdodCgpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbF9jb25kaXRpb25zLCBsYWJlbHMgPSBjKCJDb250aW51b3VzIGxpZ2h0LCBOMiBmZWVkIiwgIkNvbnRpbnVvdXMgbGlnaHQsIE8yIGZlZWQiLCAiTGlnaHQtZGFyayBjeWNsZXMsIE8yIGZlZWQiKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb25kaXRpb25zLCBsYWJlbHMgPSBjKCJDb250aW51b3VzIGxpZ2h0LCBOMiBmZWVkIiwgIkNvbnRpbnVvdXMgbGlnaHQsIE8yIGZlZWQiLCAiTGlnaHQtZGFyayBjeWNsZXMsIE8yIGZlZWQiKSkgKyBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzPWMoIkNMX04yIj0ic29saWQiLCAiQ0xfTzIiPSJkYXNoZWQiLCAiTEQiPSJkb3R0ZWQiKSwgbGFiZWxzPWMoIkNvbnRpbnVvdXMgbGlnaHQsIE4yIGZlZWQiLCAiQ29udGludW91cyBsaWdodCwgTzIgZmVlZCIsICJMaWdodC1kYXJrIGN5Y2xlcywgTzIgZmVlZCIpKSArIGxhYnModGl0bGU9IkNvbXBsZXRlIENiYk0gbGlicmFyeSIsIHg9Ik5vcm1hbGl6ZWQgZml0bmVzcyB2YWx1ZSIpIApwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi93aG9sZV9saWJyYXJ5X2RlbnNpdHlwbG90LnBkZiIsIHBsb3Q9cCwgd2lkdGg9MTEuNSwgaGVpZ2h0PTgpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy93aG9sZV9saWJyYXJ5X2RlbnNpdHlwbG90LnBuZyIsIHBsb3Q9cCwgd2lkdGg9MTEuNSwgaGVpZ2h0PTgpCmBgYAoKYGBge3J9CmZvcihjb25kIGluIHVuaXF1ZShwbG90X3N1YnNldCRjb25kaXRpb24pKXsKICBwcmludChjb25kKQogIHRlbXBfc3VicyA8LSBzdWJzZXQocGxvdF9zdWJzZXQsIHBsb3Rfc3Vic2V0JGNvbmRpdGlvbj09Y29uZCkKICBwcmludChucm93KHRlbXBfc3VicykpCiAgcHJpbnQoIkFib3ZlIDEiKQogIHByaW50KG5yb3coc3Vic2V0KHRlbXBfc3VicywgdGVtcF9zdWJzJG5vcm0gPiAxKSkpCiAgcHJpbnQoIkFib3ZlIDEsIHBlcmNlbnRhZ2UiKQogIHByaW50KG5yb3coc3Vic2V0KHRlbXBfc3VicywgdGVtcF9zdWJzJG5vcm0gPiAxKSkvbnJvdyh0ZW1wX3N1YnMpKQogIHByaW50KCJBYm92ZSAxIGFuZCBzaWduaWZpY2FudGx5IGhpZ2hlciB0aGFuIFdUIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtID4gMSAmIHRlbXBfc3VicyRwX2ZpdF9hZGpfV1QgPCAwLjA1KSkpCiAgcHJpbnQoIkFib3ZlIDEgYW5kIHNpZ25pZmljYW50bHkgaGlnaGVyIHRoYW4gV1QsIHBlcmNlbnRhZ2UiKQogIHByaW50KG5yb3coc3Vic2V0KHRlbXBfc3VicywgdGVtcF9zdWJzJG5vcm0gPiAxICYgdGVtcF9zdWJzJHBfZml0X2Fkal9XVCA8IDAuMDUpKS9ucm93KHRlbXBfc3VicykpCiAgcHJpbnQoIkJlbG93IDAiKQogIHByaW50KG5yb3coc3Vic2V0KHRlbXBfc3VicywgdGVtcF9zdWJzJG5vcm0gPCAwKSkpCiAgcHJpbnQoIkJlbG93IDAsIHBlcmNlbnRhZ2UiKQogIHByaW50KG5yb3coc3Vic2V0KHRlbXBfc3VicywgdGVtcF9zdWJzJG5vcm0gPCAwKSkvbnJvdyh0ZW1wX3N1YnMpKQp9CmBgYAoKV2hlbiBvbmx5IGNoZWNraW5nIG11dGFudCB2YXJpYW50cyBwcmVzZW50IGluIHRoZSBjb21iaW5hdG9yaWFsIGxpYnJhcnkgd2l0aCBtb3JlIHRoYW4gYSBzaW5nbGUgYW1pbm8gYWNpZCBleGNoYW5nZSwgd2Ugc3RpbGwgb2JzZXJ2ZSBhIGNsZWFyIGJpbW9kYWwgZGlzdHJpYnV0aW9uIHZlcnkgc2ltaWxhciB0byB0aGUgb25lIG9mIHRoZSBjb21wbGV0ZSBkYXRhIHNldC4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpwbG90X3N1YnNldCA8LSB1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgKGZpdG5lc3NfZGF0YSRjYXRlZ29yeSAlaW4lIGMoImNvbWJpbmF0b3JpYWwiKSkgJiBmaXRuZXNzX2RhdGEkbnVtYmVyX211dHMgPiAxKVssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAicF9maXRfYWRqX1dUIiwgImNvbmRpdGlvbiIpXSkKbGVuZ3RoKHVuaXF1ZShwbG90X3N1YnNldCRzZ1JOQV90YXJnZXQpKQpsZW5ndGgodW5pcXVlKHBsb3Rfc3Vic2V0JHNnUk5BX3RhcmdldCkpL2xlbmd0aCh1bmlxdWUoZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCkpCgpwIDwtIGdncGxvdChwbG90X3N1YnNldCwgYWVzKHg9bm9ybSwgZ3JvdXA9Y29uZGl0aW9uLCBmaWxsPWNvbmRpdGlvbiwgY29sb3I9Y29uZGl0aW9uLCBsaW5ldHlwZT1jb25kaXRpb24pKSArIGdlb21fZGVuc2l0eShhZGp1c3Q9MS41LCBhbHBoYT0uNCkgKyB0aGVtZV9saWdodCgpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJDTF9OMiI9InNvbGlkIiwgIkNMX08yIj0iZGFzaGVkIiwgIkxEIj0iZG90dGVkIiksIGxhYmVscz1jKCJDb250aW51b3VzIGxpZ2h0LCBOMiBmZWVkIiwgIkNvbnRpbnVvdXMgbGlnaHQsIE8yIGZlZWQiLCAiTGlnaHQtZGFyayBjeWNsZXMsIE8yIGZlZWQiKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y29sX2NvbmRpdGlvbnMsIGxhYmVscyA9IGMoIkNvbnRpbnVvdXMgbGlnaHQsIE4yIGZlZWQiLCAiQ29udGludW91cyBsaWdodCwgTzIgZmVlZCIsICJMaWdodC1kYXJrIGN5Y2xlcywgTzIgZmVlZCIpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sX2NvbmRpdGlvbnMsIGxhYmVscyA9IGMoIkNvbnRpbnVvdXMgbGlnaHQsIE4yIGZlZWQiLCAiQ29udGludW91cyBsaWdodCwgTzIgZmVlZCIsICJMaWdodC1kYXJrIGN5Y2xlcywgTzIgZmVlZCIpKSArIGxhYnModGl0bGU9IkNvbWJpbmF0b3JpYWwgQ2JiTSBsaWJyYXJ5IHdpdGggbW9yZSB0aGFuIG9uZSBleGNoYW5nZSIsIHg9Ik5vcm1hbGl6ZWQgZml0bmVzcyB2YWx1ZSIpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL2NvbWJpbmF0b3JpYWxfdy1vU2luZ2xlc19saWJyYXJ5X2RlbnNpdHlwbG90LnBkZiIsIHBsb3Q9cCwgd2lkdGg9MTEuNSwgaGVpZ2h0PTgpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9jb21iaW5hdG9yaWFsX3ctb1NpbmdsZXNfbGlicmFyeV9kZW5zaXR5cGxvdC5wbmciLCBwbG90PXAsIHdpZHRoPTExLjUsIGhlaWdodD04KQpgYGAKCmBgYHtyfQpmb3IoY29uZCBpbiB1bmlxdWUocGxvdF9zdWJzZXQkY29uZGl0aW9uKSl7CiAgcHJpbnQoY29uZCkKICB0ZW1wX3N1YnMgPC0gc3Vic2V0KHBsb3Rfc3Vic2V0LCBwbG90X3N1YnNldCRjb25kaXRpb249PWNvbmQpCiAgcHJpbnQobnJvdyh0ZW1wX3N1YnMpKQogIHByaW50KCJBYm92ZSAxIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtID4gMSkpKQogIHByaW50KCJBYm92ZSAxLCBwZXJjZW50YWdlIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtID4gMSkpL25yb3codGVtcF9zdWJzKSkKICBwcmludCgiQWJvdmUgMSBhbmQgc2lnbmlmaWNhbnRseSBoaWdoZXIgdGhhbiBXVCIpCiAgcHJpbnQobnJvdyhzdWJzZXQodGVtcF9zdWJzLCB0ZW1wX3N1YnMkbm9ybSA+IDEgJiB0ZW1wX3N1YnMkcF9maXRfYWRqX1dUIDwgMC4wNSkpKQogIHByaW50KCJBYm92ZSAxIGFuZCBzaWduaWZpY2FudGx5IGhpZ2hlciB0aGFuIFdULCBwZXJjZW50YWdlIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtID4gMSAmIHRlbXBfc3VicyRwX2ZpdF9hZGpfV1QgPCAwLjA1KSkvbnJvdyh0ZW1wX3N1YnMpKQogIHByaW50KCJCZWxvdyAwIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtIDwgMCkpKQogIHByaW50KCJCZWxvdyAwLCBwZXJjZW50YWdlIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtIDwgMCkpL25yb3codGVtcF9zdWJzKSkKfQpgYGAKCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpwbG90X3N1YnNldCA8LSB1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJGNhdGVnb3J5ICVpbiUgYygic2F0dXJhdGlvbmFsIiwgImNvbWJpQU5Ec2F0dXIiKSlbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgInBfZml0X2Fkal9XVCIsICJudW1fYmFyY29kZXMiLCAiY29uZGl0aW9uIildKQpsZW5ndGgodW5pcXVlKHBsb3Rfc3Vic2V0JHNnUk5BX3RhcmdldCkpCmxlbmd0aCh1bmlxdWUocGxvdF9zdWJzZXQkc2dSTkFfdGFyZ2V0KSkvbGVuZ3RoKHVuaXF1ZShmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0KSkKCnAgPC0gZ2dwbG90KHBsb3Rfc3Vic2V0LCBhZXMoeD1ub3JtLCBncm91cD1jb25kaXRpb24sIGZpbGw9Y29uZGl0aW9uLCBjb2xvcj1jb25kaXRpb24sIGxpbmV0eXBlPWNvbmRpdGlvbikpICsgZ2VvbV9kZW5zaXR5KGFkanVzdD0xLjUsIGFscGhhPS40KSArIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y29sX2NvbmRpdGlvbnMsIGxhYmVscyA9IGMoIkNvbnRpbnVvdXMgbGlnaHQsIE4yIGZlZWQiLCAiQ29udGludW91cyBsaWdodCwgTzIgZmVlZCIsICJMaWdodC1kYXJrIGN5Y2xlcywgTzIgZmVlZCIpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sX2NvbmRpdGlvbnMsIGxhYmVscyA9IGMoIkNvbnRpbnVvdXMgbGlnaHQsIE4yIGZlZWQiLCAiQ29udGludW91cyBsaWdodCwgTzIgZmVlZCIsICJMaWdodC1kYXJrIGN5Y2xlcywgTzIgZmVlZCIpKSArIGxhYnModGl0bGU9IlNhdHVyYXRpb25hbCBDYmJNIGxpYnJhcnkiLCB4PSJOb3JtYWxpemVkIGZpdG5lc3MgdmFsdWUiKSArIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXM9YygiQ0xfTjIiPSJzb2xpZCIsICJDTF9PMiI9ImRhc2hlZCIsICJMRCI9ImRvdHRlZCIpLCBsYWJlbHM9YygiQ29udGludW91cyBsaWdodCwgTjIgZmVlZCIsICJDb250aW51b3VzIGxpZ2h0LCBPMiBmZWVkIiwgIkxpZ2h0LWRhcmsgY3ljbGVzLCBPMiBmZWVkIikpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3NhdHVyYXRpb25hbF9saWJyYXJ5X2RlbnNpdHlwbG90LnBkZiIsIHBsb3Q9cCwgd2lkdGg9MTEuNSwgaGVpZ2h0PTgpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zYXR1cmF0aW9uYWxfbGlicmFyeV9kZW5zaXR5cGxvdC5wbmciLCBwbG90PXAsIHdpZHRoPTExLjUsIGhlaWdodD04KQpgYGAKCmBgYHtyfQpmb3IoY29uZCBpbiB1bmlxdWUocGxvdF9zdWJzZXQkY29uZGl0aW9uKSl7CiAgcHJpbnQoY29uZCkKICB0ZW1wX3N1YnMgPC0gc3Vic2V0KHBsb3Rfc3Vic2V0LCBwbG90X3N1YnNldCRjb25kaXRpb249PWNvbmQpCiAgcHJpbnQobnJvdyh0ZW1wX3N1YnMpKQogIHByaW50KCJBYm92ZSAxIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtID4gMSkpKQogIHByaW50KCJBYm92ZSAxLCBwZXJjZW50YWdlIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtID4gMSkpL25yb3codGVtcF9zdWJzKSkKICBwcmludCgiQWJvdmUgMSBhbmQgc2lnbmlmaWNhbnRseSBoaWdoZXIgdGhhbiBXVCIpCiAgcHJpbnQobnJvdyhzdWJzZXQodGVtcF9zdWJzLCB0ZW1wX3N1YnMkbm9ybSA+IDEgJiB0ZW1wX3N1YnMkcF9maXRfYWRqX1dUIDwgMC4wNSkpKQogIHByaW50KCJBYm92ZSAxIGFuZCBzaWduaWZpY2FudGx5IGhpZ2hlciB0aGFuIFdULCBwZXJjZW50YWdlIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtID4gMSAmIHRlbXBfc3VicyRwX2ZpdF9hZGpfV1QgPCAwLjA1KSkvbnJvdyh0ZW1wX3N1YnMpKQogIHByaW50KCJCZWxvdyAwIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtIDwgMCkpKQogIHByaW50KCJCZWxvdyAwLCBwZXJjZW50YWdlIikKICBwcmludChucm93KHN1YnNldCh0ZW1wX3N1YnMsIHRlbXBfc3VicyRub3JtIDwgMCkpL25yb3codGVtcF9zdWJzKSkKfQpgYGAKCldoZW4gY29tcGFyaW5nIGZpdG5lc3MgdmFsdWVzIGJldHdlZW4gdGhlIGNvbWJpbmF0b3JpYWwgYW5kIHNhdHVyYXRpb25hbCBwYXJ0IGRpcmVjdGx5LCBpdCBiZWNvbWVzIG9idmlvdXMgdGhhdCB0aGUgc2F0dXJhdGlvbmFsIHBhcnQgY29udGFpbnMsIHJlbGF0aXZlbHkgc3BlYWtpbmcsIG1vcmUgdmFyaWFudHMgd2l0aCBmdW5jdGlvbmFsIFJ1YmlzY28gdmFyaWFudHMsIGkuZS4gdGhlIHJpZ2h0IHBlYWsgb2YgdGhlIGJpbW9kYWwgZGlzdHJpYnV0aW9uIGlzIGhpZ2hlciB0aGFuIHRoZSBsZWZ0IHBlYWsgY29udHJhc3RpbmcgdG8gdGhlIGNhc2UgaW4gdGhlIGNvbWJpbmF0b3JpYWwgcGFydCBvZiB0aGUgbGlicmFyeS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpmaXRuZXNzX2RhdGFfMiA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkY2F0ZWdvcnkhPSJub3RFeHBlY3RlZCIgJiBmaXRuZXNzX2RhdGEkY2F0ZWdvcnkhPSJXVCIpCmZpdG5lc3NfZGF0YV8yW2ZpdG5lc3NfZGF0YV8yJGNhdGVnb3J5PT0iY29tYmlBTkRzYXR1ciIsXSRjYXRlZ29yeSA8LSAic2F0dXJhdGlvbmFsIgpmaXRuZXNzX2RhdGFfMiA8LSB1bmlxdWUoZml0bmVzc19kYXRhXzJbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgInBfZml0X2Fkal9XVCIsICJjYXRlZ29yeSIsICJjb25kaXRpb24iKV0pCnAgPC0gZ2dwbG90KGZpdG5lc3NfZGF0YV8yLCBhZXMoeD1ub3JtLCBmaWxsPWNhdGVnb3J5LCBjb2xvcj1jYXRlZ29yeSwgbGluZXR5cGU9Y2F0ZWdvcnkpKSArIGdlb21fZGVuc2l0eShhZGp1c3Q9MS41LCBhbHBoYT0uNCkgKyB0aGVtZV9saWdodCgpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoY29sX2NvbWJpX3NhdCksIGxhYmVscyA9IGMoIkNvbWJpbmF0b3JpYWwiLCAiU2F0dXJhdGlvbmFsIikpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKGNvbF9jb21iaV9zYXQpLCBsYWJlbHMgPSBjKCJDb21iaW5hdG9yaWFsIiwgIlNhdHVyYXRpb25hbCIpKSArIGxhYnMoeD0iTm9ybWFsaXplZCBmaXRuZXNzIHZhbHVlIikgKyBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzPWMoImNvbWJpbmF0b3JpYWwiPSJzb2xpZCIsICJzYXR1cmF0aW9uYWwiPSJkYXNoZWQiKSwgbGFiZWxzPWMoIkNvbWJpbmF0b3JpYWwiLCAiU2F0dXJhdGlvbmFsIikpICsgZmFjZXRfd3JhcCh+Y29uZGl0aW9uKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9kZW5zaXR5X2NvbXBhcmVDb21iaVNhdHVyLnBkZiIsIHBsb3Q9cCwgd2lkdGg9MTAsIGhlaWdodD00LCB1bml0cz0iY20iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvZGVuc2l0eV9jb21wYXJlQ29tYmlTYXR1ci5wbmciLCBwbG90PXApCmBgYAoKIyMgT3ZlcnZpZXcgZ3Jvd3RoIGFsbCB2YXJpYW50cwoKVGhlIG1lYW4gb2YgdGhlIGxvZzJGQyBvZiBlYWNoIHZhcmlhbnQgcHJlc2VudCBpbiB0aGUgbGlicmFyeSBpcyBzaG93bi4gUGluayBzaG93cyBLMjE0SCwgd2hpY2ggaXMgdW5kZXIgYWxsIHRocmVlIGNvbmRpdGlvbnMgdGhlIEsyMTQgdmFyaWFudCBwZXJmb3JtaW5nIGJlc3QsIGJsYWNrIHRoZSBXVCB2YXJpYW50LiBQdXJwbGUgc2hvd3MgSzIxNEQgYW5kIEsyMTRJLCB0aGUgdHdvIEsyMTQgdmFyaWFudHMgcGVyZm9ybWluZyB3b3JzdC4gVGhlcmUgaXMgYSBjbGVhciBzZXBhcmF0aW9uIG9mIGRpZmZlcmVudCB2YXJpYW50cywgV1QgaXMgY2xlYXJseSBnYWluaW5nIGluIHJlbGF0aXZlIGFidW5kYW5jZSBhbmQgdGhlIGludmVzdGlnYXRlZCBLMjE0IHZhcmlhbnRzIGFyZSBkcm9wcGluZy4gVGhpcyBjb25maXJtcyBzdWNjZXNzZnVsIHNlbGVjdGlvbiBhY2NvcmRpbmcgdG8gUnViaXNjbyBhY3Rpdml0eS4KCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmZpdF9mYWN0b3IgPC0gZml0bmVzc19kYXRhCmZpdF9mYWN0b3Ikc2dSTkFfdGFyZ2V0IDwtIGZhY3RvcihmaXRfZmFjdG9yJHNnUk5BX3RhcmdldCwgbGV2ZWxzPWModW5pcXVlKHN1YnNldChmaXRfZmFjdG9yLCAhZml0X2ZhY3RvciRzZ1JOQV90YXJnZXQgJWluJSBjKCJXVCIsICJLMjE0UiIpKSRzZ1JOQV90YXJnZXQpLCAiV1QiLCAiSzIxNFIiKSkKcCA8LSBnZ3Bsb3QoZml0X2ZhY3RvciwgYWVzKHg9dGltZSwgeT13bWVhbl9sb2cyRm9sZENoYW5nZSwgZ3JvdXA9c2dSTkFfdGFyZ2V0LCBjb2xvcj1zZ1JOQV90YXJnZXQsIGxpbmV3aWR0aD1zZ1JOQV90YXJnZXQsIGFscGhhPXNnUk5BX3RhcmdldCkpICsgZ2VvbV9saW5lKCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIksyMTRSIj0icHVycGxlIiwgIldUIj0iYmxhY2siKSwgbmEudmFsdWU9ImxpZ2h0Z3JheSIpICsgdGhlbWVfbGlnaHQoKSArIHhsYWIoIlRpbWUgKGdlbmVyYXRpb25zKSIpICsgeWxhYigibG9nMkZDKHRvIGc9MCkiKSArIHNjYWxlX2Rpc2NyZXRlX21hbnVhbCgibGluZXdpZHRoIiwgdmFsdWVzPWMoIksyMTRSIj0wLjMsICJXVCI9MC4zKSwgbmEudmFsdWU9MC4xKSArIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXM9YygiSzIxNFIiPTEsICJXVCI9MSksIG5hLnZhbHVlPTAuNSkgKyBmYWNldF93cmFwKH5jb25kaXRpb24pICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCkpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL2FsbF9zZ1JOQXRhcmdldHNfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCwgaGVpZ2h0PTEuNSwgd2lkdGg9NSwgdW5pdHM9ImluIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL2FsbF9zZ1JOQXRhcmdldHNfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCwgaGVpZ2h0PTEuNSwgd2lkdGg9NSwgdW5pdHM9ImluIikKYGBgCgojIyBQbG90IFdUIGFuZCBLMjE0IHZhcmlhbnRzCgpgYGB7ciBwbG90dGluZy1mdW5jdGlvbnMtbW9yZUNvbG9yLUNJLCBlY2hvPUZBTFNFfQp5bGltcyA8LSBjKC02LjcsIDguMCkKCmxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIgPC0gZnVuY3Rpb24oZGZfdG9fcGxvdCl7CiAgc2VfdmVjdG9yIDwtIGRmX3RvX3Bsb3Qkc2RfbG9nMkZvbGRDaGFuZ2Uvc3FydChkZl90b19wbG90JG51bV9iYXJjb2RlcykKICBzZV92ZWN0b3JbaXMubmEoc2VfdmVjdG9yKV0gPC0gZGZfdG9fcGxvdCRsZmNTRVtpcy5uYShzZV92ZWN0b3IpXQogIGRmX3RvX3Bsb3QkY29uZiA8LSBxbm9ybSgwLjk3NSkqc2VfdmVjdG9yCiAgcGxvdF9wIDwtIGdncGxvdChkZl90b19wbG90LCBhZXMoeD10aW1lLCB5PXdtZWFuX2xvZzJGb2xkQ2hhbmdlLCB5bWluPXdtZWFuX2xvZzJGb2xkQ2hhbmdlLWNvbmYsIHltYXg9d21lYW5fbG9nMkZvbGRDaGFuZ2UrY29uZiwgZ3JvdXA9c2dSTkFfdGFyZ2V0LCBjb2xvcj1mYWN0b3Ioc2dSTkFfdGFyZ2V0KSwgZmlsbD1mYWN0b3Ioc2dSTkFfdGFyZ2V0KSwgbGluZXR5cGU9ZmFjdG9yKHNnUk5BX3RhcmdldCkpKSArIG5ld19zY2FsZSgiYWxwaGEiKSArIGdlb21fbGluZShsaW5ld2lkdGg9MC4zKSArIGdlb21fcmliYm9uKGxpbmV0eXBlPTAsIGFscGhhPTAuMjUpICsgdGhlbWVfbGlnaHQoKSArIHhsYWIoIlRpbWUgKGdlbmVyYXRpb25zKSIpICsgeWxhYigiTG9nMkZDIikgKyBjb29yZF9jYXJ0ZXNpYW4oeWxpbT15bGltcykgKyBmYWNldF93cmFwKH5jb25kaXRpb24pICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKICByZXR1cm4ocGxvdF9wKQp9CgpsaW5lcGxvdF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMiA8LSBmdW5jdGlvbihkZl90b19wbG90KXsKICBwbG90X3AgPC0gZ2dwbG90KGRmX3RvX3Bsb3QsIGFlcyh4PXRpbWUsIHk9d21lYW5fbG9nMkZvbGRDaGFuZ2UsIGNvbG9yPWZhY3RvcihzZ1JOQV90YXJnZXQpKSkgKyBuZXdfc2NhbGUoImFscGhhIikgKyBnZW9tX2xpbmUobGluZXdpZHRoPTAuMykgKyB0aGVtZV9saWdodCgpICsgeGxhYigiVGltZSAoZ2VuZXJhdGlvbnMpIikgKyB5bGFiKCJMb2cyRkMiKSArIGZhY2V0X3dyYXAofmNvbmRpdGlvbikgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBjb29yZF9jYXJ0ZXNpYW4oeWxpbT15bGltcykKICByZXR1cm4ocGxvdF9wKQp9CgpsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX2xvZzIgPC0gZnVuY3Rpb24oZGZfdG9fcGxvdCl7CiAgZGZfdG9fcGxvdCRjb25mIDwtIHFub3JtKDAuOTc1KSpkZl90b19wbG90JGxmY1NFCiAgYWxwaGFfdmFsdWVzIDwtIDEtZGZfdG9fcGxvdCR3ZWlnaHRfbGZjU0UKICBuYW1lcyhhbHBoYV92YWx1ZXMpIDwtIGRmX3RvX3Bsb3Qkc2dSTkEKICBwbG90X3AgPC0gZ2dwbG90KGRmX3RvX3Bsb3QsIGFlcyh4PXRpbWUsIHk9bG9nMkZvbGRDaGFuZ2UsIHltaW49bG9nMkZvbGRDaGFuZ2UtY29uZiwgeW1heD1sb2cyRm9sZENoYW5nZStjb25mLCBncm91cD1zZ1JOQSwgY29sb3I9ZmFjdG9yKHNnUk5BKSwgZmlsbD1mYWN0b3Ioc2dSTkEpKSkgKyBuZXdfc2NhbGUoImFscGhhIikgKyBnZW9tX2xpbmUobGluZXdpZHRoPTAuMykgKyBnZW9tX3JpYmJvbihsaW5ldHlwZT0wLCBhZXMoYWxwaGE9c2dSTkEpKSArIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXM9YWxwaGFfdmFsdWVzKSArIHRoZW1lX2xpZ2h0KCkgKyB4bGFiKCJUaW1lIChnZW5lcmF0aW9ucykiKSArIHlsYWIoIkxvZzJGQyIpICsgZmFjZXRfd3JhcCh+Y29uZGl0aW9uKSAgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBjb29yZF9jYXJ0ZXNpYW4oeWxpbT15bGltcykKICByZXR1cm4ocGxvdF9wKQp9CgpsaW5lcGxvdF9zZXZlcmFsQ29sb3Vyc19sb2cyIDwtIGZ1bmN0aW9uKGRmX3RvX3Bsb3QpewogIHBsb3RfcCA8LSBnZ3Bsb3QoZGZfdG9fcGxvdCwgYWVzKHg9dGltZSwgeT1sb2cyRm9sZENoYW5nZSwgZ3JvdXA9c2dSTkEsIGNvbG9yPWZhY3RvcihzZ1JOQSkpKSArIG5ld19zY2FsZSgiYWxwaGEiKSArIGdlb21fbGluZShsaW5ld2lkdGg9MC4zKSArIHRoZW1lX2xpZ2h0KCkgKyB4bGFiKCJUaW1lIChnZW5lcmF0aW9ucykiKSArIHlsYWIoIkxvZzJGQyIpICsgZmFjZXRfd3JhcCh+Y29uZGl0aW9uKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKSArIGNvb3JkX2NhcnRlc2lhbih5bGltPXlsaW1zKQogIHJldHVybihwbG90X3ApCn0KYGBgCgpUaGUgOTUlIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIHRoZSB3ZWlnaHRlZCBtZWFuIGxvZzJGQyBvZiBhbGwgV1QgYmFyY29kZXMgaXMgcmVsYXRpdmVseSBzbWFsbCwgbWVhbmluZyB0aGF0IHdlIGNhbiByZWx5IHJlbGF0aXZlbHkgd2VsbCBvbiB0aGUgcmVzcGVjdGl2ZSBkYXRhLiBJbiB0b3RhbCwgMzAgZGlmZmVyZW50IGJhcmNvZGVzIGZvciBXVCB3ZXJlIHRha2VuIGludG8gYWNjb3VudC4gVGhlc2Ugd2VyZSB0aGUgaGlnaGVzdCAzMCBiYXJjb2RlcyBhc3NpZ25lZCB0byB0aGUgV1QgdmFyaWFudCBwcmVzZW50IGluIHRoZSBjb21wbGV0ZSBkYXRhIHNldC4gVGhlIGhpZ2ggbnVtYmVyIG9mIFdUIGJhcmNvZGVzIGFzIHdlbGwgYXMgdGhlaXIgaGlnaCBjb3VudCBudW1iZXJzIGJvdGggY29udHJpYnV0ZSB0byBhIHJlbGlhYmxlIGVzdGltYXRlIG9mIHRoZSBjb3JyZXNwb25kaW5nIGZpdG5lc3MuCgpgYGB7ciAgbWVzc2FnZT1GQUxTRX0KV1Rfc3Vic2V0IDwtIHVuaXF1ZShzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0PT0iV1QiKVssYygibnVtX2JhcmNvZGVzIiwgInNkX2xvZzJGb2xkQ2hhbmdlIiwgImxmY1NFIiwgInRpbWUiLCAid21lYW5fbG9nMkZvbGRDaGFuZ2UiLCAic2dSTkFfdGFyZ2V0IiwgInNnUk5BIiwgImxvZzJGb2xkQ2hhbmdlIiwgIndlaWdodF9sZmNTRSIsICJjb25kaXRpb24iKV0pCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihXVF9zdWJzZXQpICsgbGFicyh0aXRsZT0iV1Qgd2l0aCA5NSUgQ0kiKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9XVF90aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvV1RfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCkKYGBgCgpIZXJlLCB0aGUgbG9nMkZDIGNoYXJ0cyBvZiB0aGUgZGlmZmVyZW50IGJhcmNvZGVzIHVzZWQgZm9yIGRldGVybWluaW5nIFdUIGRhdGEgYXJlIHBsb3R0ZWQuIDk1JSBjb25maWRlbmNlIGludGVydmFscyBhcmUgY29sb3JlZCBhY2NvcmRpbmcgdG8gdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBsb2cyRkMgLSB0aGUgbW9yZSB0cmFuc3BhcmVudCwgdGhlIGhpZ2hlciB0aGUgc3RhbmRhcmQgZXJyb3IgYW5kIHRoZSBvdGhlciB3YXkgcm91bmQuIFRoZSBiYXJjb2RlcyBkZXZpYXRpbmcgZnJvbSB0aGUgb3ZlcmFsbCBzdGFuZGFyZCBoYXZlIGhpZ2hlciBzdGFuZGFyZCBlcnJvcnMgdGhhbiB0aGUgYmFyY29kZXMgd2hpY2ggYXJlIGNsb3NlIHRvIHRoZSBvdmVyYWxsIG1lYW4uIEluIGJsYWNrLCB0aGUgb3ZlcmFsbCB0cmVuZCBpcyBhZGRlZC4KCkl0IHNlZW1zIGFzIGlmIG1vc3Qgb2YgdGhlIDMwIGJhcmNvZGVzIGFncmVlZCB3ZWxsLiBOb3RlIHRoYXQsIGZvciBjb250aW51b3VzIGxpZ2h0IGFuZCBhbiBPMi1jb250YWluaW5nIGZlZWQsIHNldmVyYWwgYmFyY29kZXMgZ28gYSBiaXQgZG93biBmb3IgdGhlIGxhc3QgdGltZSBwb2ludC4gSXQgc2VlbXMgYXMgaWYgc2VsZWN0aW9uIHdhcyBhbHJlYWR5IGxvc3QgdGhlbiBvciBhdCBsZWFzdCBnb3QgbXVjaCB3ZWFrZXIuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX2xvZzIoV1Rfc3Vic2V0KSArIGxhYnModGl0bGU9IldUIGJhcmNvZGVzIHdpdGggOTUlIENJIikgKyBnZW9tX2xpbmUoYWVzKHg9dGltZSwgeT13bWVhbl9sb2cyRm9sZENoYW5nZSwgZ3JvdXA9c2dSTkFfdGFyZ2V0KSwgY29sb3I9ImJsYWNrIikKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvV1RfYmFyY29kZXNfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL1dUX2JhcmNvZGVzX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXApCgpsaW5lcGxvdF9zZXZlcmFsQ29sb3Vyc19sb2cyKFdUX3N1YnNldCkgKyBsYWJzKHRpdGxlPSJXVCBiYXJjb2RlcyIpICsgZ2VvbV9saW5lKGFlcyh4PXRpbWUsIHk9d21lYW5fbG9nMkZvbGRDaGFuZ2UsIGdyb3VwPXNnUk5BX3RhcmdldCksIGNvbG9yPSJibGFjayIpCmBgYAoKSGVyZSwgYWxsIEsyMTQgbXV0YW50IHZhcmlhbnRzIGFuZCB0aGVpciB3ZWlnaHRlZCBtZWFuIGxvZzJGb2xkQ2hhbmdlIGFyZSBwbG90dGVkLiBBbGwgaGF2ZSBhIHRlbmRlbmN5IHRvIGRlY3JlYXNlIGluIHJlbGF0aXZlIGFidW5kYW5jZS4gRm9yIGNvbnRpbnVvdXMgbGlnaHQgYW5kIGFuIE8yLWNvbnRhaW5pbmcgZ2FzIGZlZWQsIHRoZSBsYXN0IHRpbWUgcG9pbnQgbG9va3MgYXMgaWYgc2VsZWN0aW9uIGdvdCB3b3JzZS4gU2FtZSBhbHNvIGhvbGRzLCB0byBzb21lIGRlZ3JlZSwgZm9yIHRoZSBOMi1jb250YWluaW5nIGZlZWQuIEsyMTRIIGdvZXMgcHJldHR5IHdpbGQgaW4gdGhlIExEIGNvbmRpdGlvbi4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpLMjE0X3N1YnNldCA8LSB1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZ3JlcGwoIksyMTQiLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0KSlbLGMoIm51bV9iYXJjb2RlcyIsICJzZF9sb2cyRm9sZENoYW5nZSIsICJsZmNTRSIsICJ0aW1lIiwgIndtZWFuX2xvZzJGb2xkQ2hhbmdlIiwgInNnUk5BX3RhcmdldCIsICJzZ1JOQSIsICJsb2cyRm9sZENoYW5nZSIsICJ3ZWlnaHRfbGZjU0UiLCAiY29uZGl0aW9uIiwgIm5vcm0iKV0pCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihLMjE0X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJLMjE0IHZhcmlhbnRzIHdpdGggOTUlIENJIikKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvSzIxNF92YXJpYW50c190aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvSzIxNF92YXJpYW50c190aW1lTGluZVBsb3QucG5nIiwgcGxvdD1wKQoKbGluZXBsb3Rfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIoSzIxNF9zdWJzZXQpICsgbGFicyh0aXRsZT0iSzIxNCB2YXJpYW50cyIpCmBgYAoKSzIxNEggaXMgdGhlIEsyMTQgdmFyaWFudCB3aXRoIHRoZSBoaWdoZXN0IGZpdG5lc3Mgc2NvcmUgdW5kZXIgYWxsIHRocmVlIGludmVzdGlnYXRlZCBjb25kaXRpb25zLiBJdHMgZml0bmVzcyB2YWx1ZSB3YXMgZGV0ZXJtaW5lZCBvbiBiYXNpcyBvZiAzIGJhcmNvZGVzLCBvZiB3aGljaCBvbmUgc2hvd3MgYSBzbG93ZXIgZGVjcmVhc2UgdGhhbiB0aGUgb3RoZXIgMiAoaW4gQ0xfTzIgYW5kIExELCBpdCBkb2VzIG5vdCBldmVuIHNob3cgYSBkZWNyZWFzZSksIGJ1dCBoYXMgYSBsb3dlciBzdGFuZGFyZCBlcnJvci4gSW4gYmxhY2ssIHRoZSB3ZWlnaHRlZCBtZWFuIGxvZzJGQyBpcyBzaG93bi4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpLMjE0SF9zdWJzZXQgPC0gdW5pcXVlKHN1YnNldChmaXRuZXNzX2RhdGEsIGdyZXBsKCJLMjE0SCIsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQpKVssYygibnVtX2JhcmNvZGVzIiwgInNkX2xvZzJGb2xkQ2hhbmdlIiwgImxmY1NFIiwgInRpbWUiLCAid21lYW5fbG9nMkZvbGRDaGFuZ2UiLCAic2dSTkFfdGFyZ2V0IiwgInNnUk5BIiwgImxvZzJGb2xkQ2hhbmdlIiwgIndlaWdodF9sZmNTRSIsICJjb25kaXRpb24iKV0pCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19sb2cyKEsyMTRIX3N1YnNldCkgKyBsYWJzKHRpdGxlPSJLMjE0SCBiYXJjb2RlcyB3aXRoIDk1JSBDSSIpICsgZ2VvbV9saW5lKGFlcyh4PXRpbWUsIHk9d21lYW5fbG9nMkZvbGRDaGFuZ2UsIGdyb3VwPXNnUk5BX3RhcmdldCksIGNvbG9yPSJibGFjayIpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL0syMTRIX2JhcmNvZGVzX3RpbWVMaW5lUGxvdC5wZGYiLCBwbG90PXApCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9LMjE0SF9iYXJjb2Rlc190aW1lTGluZVBsb3QucG5nIiwgcGxvdD1wKQpgYGAKCkNvbXBhcmUgSzIxNEggdG8gV1QuIEluIExELCBkdWUgdG8gdGhlIHN0cm9uZ2x5IHJpc2luZyBiYXJjb2RlLCBLMjE0SCBhbmQgV1QgYXJlIG5vdCBzaWduaWZpY2FudGx5IGRpZmZlcmVudCAoaWYgd2UgdGFrZSA5NSUgY29uZmlkZW5jZSBpbnRlcnZhbHMgYXMgbWVhc3VyZSBmb3IgIkNvdWxkIGl0IGV2ZW4gYmUgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIikuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKGJpbmRfcm93cyhXVF9zdWJzZXQsSzIxNEhfc3Vic2V0KSkgKyBsYWJzKHRpdGxlPSJLMjE0SCBhbmQgV1QiKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9XVF9LMjE0SF90aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvV1RfSzIxNEhfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCkKYGBgCgpLMjE0IHZhcmlhbnRzIEsyMTREIChMRCksIEsyMTRSIChDTF9OMikgYW5kIEsyMTRFIChDTF9PMikgd2VyZSB0aGUgb25lcyB3aGljaCB3ZXJlIHVzZWQgZm9yIG1lZGlhbiBub3JtYWxpemF0aW9uIChjb21wYXJlIGNvbXBhcmVfd2VpZ2h0aW5nU3RyYXRlZ2llc18ybmRSb3VuZC5odG1sKS4gQWxsIGxvb2sgYXMgaWYgdGhleSB3ZXJlIGdvb2QgbmVnYXRpdmUgY29udHJvbHMgaW4gc3Vic2VxdWVudCBwbG90cy4gSzIxNFIgaXMgY2xvc2VzdCB0byBhIG5vcm1hbGl6ZWQgZml0bmVzcyB2YWx1ZSBvZiAwIG9mIHRoZXNlIHRocmVlLiBXaGVuIGNhbGN1bGF0aW5nIHRoZSBhYnNvbHV0ZSBkaXN0YW5jZSB0byAwIG9mIGFsbCB2YXJpYW50cyBpbiB0aGUgbm9ybWFsaXplZCBkYXRhIHNldCwgSzIxNEwgYWxzbyBzZWVtcyB0byBiZSBhIGdvb2QgY2FuZGlkYXRlLiBTaW5jZSBLMjE0UiBoYXMgYSBsb3dlciBHaW5pIGluZGV4IGFuZCBhIGNvbXBhcmFibGUgYWJzb2x1dGUgZGlzdGFuY2UgdG8gMCBpbiBhbGwgZGF0YSBzZXRzLCBLMjE0UiB3aWxsIGJlIHBsb3R0ZWQgYXMgbmVnYXRpdmUgY29udHJvbCBzdWJzZXF1ZW50bHkuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KSzIxNERSVF9zdWJzZXQgPC0gdW5pcXVlKHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJLMjE0RCIsICJLMjE0UiIsICJLMjE0RSIsICJLMjE0TCIpKVssYygibnVtX2JhcmNvZGVzIiwgInNkX2xvZzJGb2xkQ2hhbmdlIiwgImxmY1NFIiwgInRpbWUiLCAid21lYW5fbG9nMkZvbGRDaGFuZ2UiLCAic2dSTkFfdGFyZ2V0IiwgInNnUk5BIiwgImxvZzJGb2xkQ2hhbmdlIiwgIndlaWdodF9sZmNTRSIsICJjb25kaXRpb24iLCAibm9ybSIpXSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKGJpbmRfcm93cyhXVF9zdWJzZXQsSzIxNERSVF9zdWJzZXQpKSArIGxhYnModGl0bGU9IksyMTQgdmFyaWFudHMgYW5kIFdUIikKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvV1RfSzIxNERSVExfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL1dUX0syMTREUlRMX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXApCgpwaXZvdF93aWRlcih1bmlxdWUoSzIxNERSVF9zdWJzZXRbLGMoInNnUk5BX3RhcmdldCIsICJjb25kaXRpb24iLCAibm9ybSIpXSksIHZhbHVlc19mcm9tPW5vcm0sIG5hbWVzX2Zyb209Y29uZGl0aW9uKQoKSzIxNF9zZXQgPC0gcGl2b3Rfd2lkZXIodW5pcXVlKEsyMTRfc3Vic2V0WyxjKCJzZ1JOQV90YXJnZXQiLCAiY29uZGl0aW9uIiwgIm5vcm0iKV0pLCB2YWx1ZXNfZnJvbT1ub3JtLCBuYW1lc19mcm9tPWNvbmRpdGlvbikKSzIxNF9zZXQkc3VtIDwtIGFwcGx5KGFicyhLMjE0X3NldFssYygyOjQpXSksIDEsIHN1bSkKSzIxNF9zZXQkZ2luaSA8LSBhcHBseShhYnMoSzIxNF9zZXRbLGMoMjo0KV0pLCAxLCBHaW5pKQpLMjE0X3NldFtvcmRlcihLMjE0X3NldCRzdW0pLF0KCksyMTRMX3N1YnNldCA8LSB1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIksyMTRMIiwgIksyMTRSIikpWyxjKCJudW1fYmFyY29kZXMiLCAic2RfbG9nMkZvbGRDaGFuZ2UiLCAibGZjU0UiLCAidGltZSIsICJ3bWVhbl9sb2cyRm9sZENoYW5nZSIsICJzZ1JOQV90YXJnZXQiLCAic2dSTkEiLCAibG9nMkZvbGRDaGFuZ2UiLCAid2VpZ2h0X2xmY1NFIiwgImNvbmRpdGlvbiIsICJub3JtIildKQpsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKGJpbmRfcm93cyhXVF9zdWJzZXQsSzIxNExfc3Vic2V0KSkgKyBsYWJzKHRpdGxlPSJLMjE0TCwgSzIxNFIgYW5kIFdUIikKbmVnX2NvbnRyb2xfSzIxNCA8LSAiSzIxNFIiCmBgYAoKQWxsIEsyMTQgdmFyaWFudHMgYW5kIFdUCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KcCA8LSBsaW5lcGxvdF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihiaW5kX3Jvd3MoSzIxNF9zdWJzZXQsIFdUX3N1YnNldCkpICsgbGFicyh0aXRsZT0iSzIxNCB2YXJpYW50cyBhbmQgV1QiKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiV1QiPSJibGFjayIpLCBuYS52YWx1ZT0ibGlnaHRncmF5IikKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvV1RfSzIxNF92YXJpYW50c190aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvV1RfSzIxNF92YXJpYW50c190aW1lTGluZVBsb3QucG5nIiwgcGxvdD1wKQpgYGAKCiMjIEJhcnBsb3RzIG51bWJlciBtdXRhdGlvbnMgfiBmaXRuZXNzCgpXaGVuIGludmVzdGlnYXRpbmcgYWxsIG11dGFudCB2YXJpYW50cyBwcmVzZW50IGluIHRoZSBsaWJyYXJ5LCBpbmNsdWRpbmcgdGhlIG9uZXMgd2hpY2ggb2NjdXJyZWQgc3BvbnRhbmVvdXNseSwgdGhlcmUgaXMgYSBjbGVhciB0cmVuZCBvYnNlcnZhYmxlIHRoYXQgdGhlIG1vcmUgYW1pbm8gYWNpZCBleGNoYW5nZXMvbXV0YXRpb25zIHdlcmUgaW50cm9kdWNlZCwgdGhlIGxvd2VyIHRoZSBtZWFuIGZpdG5lc3MuIFRoaXMgbWF0Y2hlcyB3ZWxsIHdpdGggd2hhdCB3YXMgcmVwb3J0ZWQgaW4gbGl0ZXJhdHVyZSBiZWZvcmUuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KcmVkX2ZpdG5lc3MgPC0gdW5pcXVlKGZpdG5lc3NfZGF0YVtjKCJzZ1JOQV90YXJnZXQiLCAibWVhbl9maXRuZXNzIiwgImNvbmRpdGlvbiIsICJjYXRlZ29yeSIsICJudW1iZXJfbXV0cyIsICJub3JtIiwgInBfZml0X2Fkal9XVCIsICJudW1fYmFyY29kZXMiKV0pCnAgPC0gZ2dwbG90KHJlZF9maXRuZXNzLCBhZXMoeD1udW1iZXJfbXV0cywgeT1ub3JtLCBncm91cD1udW1iZXJfbXV0cywgY29sb3VyPW51bWJlcl9tdXRzLCBmaWxsPW51bWJlcl9tdXRzKSkgKyBnZW9tX3BvaW50KHNpemU9MC4xLCBwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXJkb2RnZShkb2RnZS53aWR0aD0wLjkpLCBjb2xvcj0iIzRhNGE0YWZmIikgKyB0aGVtZV9saWdodCgpICsgc3RhdF9zdW1tYXJ5KGZ1bj0ibWVhbiIsIGdlb209ImJhciIsIGFscGhhPTAuMywgcG9zaXRpb249cG9zaXRpb25fZG9kZ2Uod2lkdGg9MC45KSwgbGluZXdpZHRoPTAsIGNvbG9yPSIjNGE0YTRhZmYiLCBmaWxsPSIjNGE0YTRhZmYiKSArIHlsYWIoIldlaWdodGVkIG1lYW4gZml0bmVzcyIpICsgZmFjZXRfd3JhcCh+Y29uZGl0aW9uKSsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9iYXJfZml0bmVzc19kYXRhX251bWJlck11dHNfY29tcGxldGUucGRmIiwgcCwgd2lkdGg9MzAsIGhlaWdodD0xOCwgdW5pdHM9ImNtIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL2Jhcl9maXRuZXNzX2RhdGFfbnVtYmVyTXV0c19jb21wbGV0ZS5wbmciLCBwLCB3aWR0aD0zMCwgaGVpZ2h0PTE4LCB1bml0cz0iY20iKQpgYGAKCldoZW4gY2hlY2tpbmcgdGhlIHdob2xlICJleHBlY3RlZCIgbGlicmFyeSwgaS5lLiB0aGUgY29tYmluYXRpb24gb2Ygc2F0dXJhdGlvbmFsIGFuZCBjb21iaW5hdG9yaWFsIGxpYnJhcnksIHRoaXMgdHJlbmQgaXMgc3RpbGwgb2JzZXJ2YWJsZS4KCmBgYHtyIGJhcl9maXRuZXNzX2RhdGFfTjIsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpleHBlY3RlZF9yZWRfZml0bmVzcyA8LSBzdWJzZXQocmVkX2ZpdG5lc3MsICFyZWRfZml0bmVzcyRjYXRlZ29yeT09Im5vdEV4cGVjdGVkIikKcCA8LSBnZ3Bsb3QoZXhwZWN0ZWRfcmVkX2ZpdG5lc3MsIGFlcyh4PW51bWJlcl9tdXRzLCB5PW5vcm0sIGZpbGw9bnVtYmVyX211dHMsIGNvbG91cj1udW1iZXJfbXV0cywgZ3JvdXA9bnVtYmVyX211dHMpKSArIHN0YXRfc3VtbWFyeShmdW49Im1lYW4iLCBnZW9tPSJiYXIiLCBhbHBoYT0wLjMsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuOSksIGxpbmV3aWR0aD0wLCBjb2xvcj0iIzRhNGE0YWZmIiwgZmlsbD0iIzRhNGE0YWZmIikgKyBnZW9tX3BvaW50KHNpemU9MC4wNSwgYWxwaGE9MC43LCBwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXJkb2RnZShkb2RnZS53aWR0aD0wLjkpLCBjb2xvcj0iIzRhNGE0YWZmIikgKyB0aGVtZV9saWdodCgpICsgeWxhYigiTm9ybWFsaXplZCBmaXRuZXNzIHNjb3JlIikgKyB4bGFiKCJOdW1iZXIgb2YgaW50b2R1Y2VkIGFtaW5vIGFjaWQgZXhjaGFuZ2VzIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIGZhY2V0X3dyYXAofmNvbmRpdGlvbikgKyBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKC0wLjIsNy4yKSkrIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvYmFyX2ZpdG5lc3NfZGF0YV9udW1iZXJNdXRzX29ubHlFeHBlY3RlZC5wZGYiLCBwLCB3aWR0aD01LCBoZWlnaHQ9Mi40LCB1bml0cz0iaW4iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvYmFyX2ZpdG5lc3NfZGF0YV9udW1iZXJNdXRzX29ubHlFeHBlY3RlZC5wbmciLCBwLCB3aWR0aD01LCBoZWlnaHQ9MywgdW5pdHM9ImluIikKYGBgCgpXaGVuIG9ubHkgY29tcGFyaW5nIHRoZSBhbWlubyBhY2lkIGV4Y2hhbmdlcyBwcmVzZW50IGluIHRoZSBjb21iaW5hdG9yaWFsIGxpYnJhcnksIHRoZSBvdmVyYWxsIHRyZW5kIHRoYXQgaW50cm9kdWNpbmcgYW5vdGhlciBhbWlubyBhY2lkIGV4Y2hhbmdlIGRlY3JlYXNlcyB0aGUgZml0bmVzcyB2YWx1ZSBiZWNvbWVzIGV2ZW4gY2xlYXJlci4gQXQgdGhlIHNhbWUgdGltZSwgYWRkaXRpb25hbGx5IGludHJvZHVjZWQgZXhjaGFuZ2VzIGRvIG5vdCByZWR1Y2UgdGhlIG1heGltdW0gZml0bmVzcyB2YWx1ZS4KCmBgYHtyIGJhcl9maXRuZXNzX2RhdGFfb25seUNvbWJpLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KY29tYmluYXRvcmlhbF93aWRlIDwtIHN1YnNldChleHBlY3RlZF9yZWRfZml0bmVzcywgIWV4cGVjdGVkX3JlZF9maXRuZXNzJGNhdGVnb3J5PT0ic2F0dXJhdGlvbmFsIikKbG1fZml0bmVzc19kYXRhIDwtIGxtKG5vcm0gfiBudW1iZXJfbXV0cywgY29tYmluYXRvcmlhbF93aWRlKQpzdW1tYXJ5KGxtX2ZpdG5lc3NfZGF0YSkKCmxtX2ZpdG5lc3NfZGF0YSA8LSBsbShub3JtIH4gbnVtYmVyX211dHMsIHN1YnNldChjb21iaW5hdG9yaWFsX3dpZGUsIGNvbWJpbmF0b3JpYWxfd2lkZSRjb25kaXRpb24gPT0gIkNMX04yIikpCnN1bW1hcnkobG1fZml0bmVzc19kYXRhKQoKbG1fZml0bmVzc19kYXRhIDwtIGxtKG5vcm0gfiBudW1iZXJfbXV0cywgc3Vic2V0KGNvbWJpbmF0b3JpYWxfd2lkZSwgY29tYmluYXRvcmlhbF93aWRlJGNvbmRpdGlvbiA9PSAiQ0xfTzIiKSkKc3VtbWFyeShsbV9maXRuZXNzX2RhdGEpCgpsbV9maXRuZXNzX2RhdGEgPC0gbG0obm9ybSB+IG51bWJlcl9tdXRzLCBzdWJzZXQoY29tYmluYXRvcmlhbF93aWRlLCBjb21iaW5hdG9yaWFsX3dpZGUkY29uZGl0aW9uID09ICJMRCIpKQpzdW1tYXJ5KGxtX2ZpdG5lc3NfZGF0YSkKCnAgPC0gZ2dwbG90KGNvbWJpbmF0b3JpYWxfd2lkZSwgYWVzKHg9bnVtYmVyX211dHMsIHk9bm9ybSwgZmlsbD1udW1iZXJfbXV0cywgY29sb3VyPW51bWJlcl9tdXRzKSkgKyBzdGF0X3N1bW1hcnkoZnVuPSJtZWFuIiwgZ2VvbT0iYmFyIiwgYWxwaGE9MC4zLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjkpLCBsaW5ld2lkdGg9MCwgY29sb3I9IiM0YTRhNGFmZiIsIGZpbGw9IiM0YTRhNGFmZiIpICsgZ2VvbV9wb2ludChzaXplPTAuMSwgcG9zaXRpb249cG9zaXRpb25faml0dGVyZG9kZ2UoZG9kZ2Uud2lkdGg9MC45KSwgY29sb3I9IiM0YTRhNGFmZiIpICsgdGhlbWVfbGlnaHQoKSArIHlsYWIoIk5vcm1hbGl6ZWQgZml0bmVzcyBzY29yZSIpKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICsgZmFjZXRfd3JhcCh+Y29uZGl0aW9uKSArIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoLTAuMiw3LjIpKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9iYXJfZml0bmVzc19kYXRhX251bWJlck11dHNfb25seUNvbWJpLnBkZiIsIHAsIHdpZHRoPTMwLCBoZWlnaHQ9MTgsIHVuaXRzPSJjbSIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9iYXJfZml0bmVzc19kYXRhX251bWJlck11dHNfb25seUNvbWJpLnBuZyIsIHAsIHdpZHRoPTMwLCBoZWlnaHQ9MTgsIHVuaXRzPSJjbSIpCmBgYAoKV2hlbiBvbmx5IGNoZWNraW5nIHZhcmlhbnRzIHdpdGggZml0bmVzcyB2YWx1ZXMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgdG8gdGhlICJ3aWxkLXR5cGUiIHNlcXVlbmNlLCB0aGlzIHBhdHRlcm4gZ2V0cyBkYW1wZW5lZCwgZXZlbiB0aG91Z2ggdGhlcmUgaXMgc3RpbGwgc29tZSB0cmVuZCBpbiB0aGlzIGRpcmVjdGlvbiBvYnNlcnZhYmxlIHdpdGggbW9yZSBhbmQgbW9yZSB2YXJpYW50cyBzaG93aW5nIHJlbGF0aXZlbHkgaGlnaCBzY29yZXMuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KY29tYmluYXRvcmlhbF93aWRlIDwtIHN1YnNldChjb21iaW5hdG9yaWFsX3dpZGUsIGNvbWJpbmF0b3JpYWxfd2lkZSRwX2ZpdF9hZGpfV1Q8MC4wNSkKCnAgPC0gZ2dwbG90KGNvbWJpbmF0b3JpYWxfd2lkZSwgYWVzKHg9bnVtYmVyX211dHMsIHk9bm9ybSwgZmlsbD1udW1iZXJfbXV0cywgY29sb3VyPW51bWJlcl9tdXRzKSkgKyBnZW9tX3BvaW50KHNpemU9MC4xLCBwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXJkb2RnZShkb2RnZS53aWR0aD0wLjkpKSArIHRoZW1lX2xpZ2h0KCkgKyBzdGF0X3N1bW1hcnkoZnVuPSJtZWFuIiwgZ2VvbT0iYmFyIiwgYWxwaGE9MC4zLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSh3aWR0aD0wLjkpLCBsaW5ld2lkdGg9MCkgKyB5bGFiKCJXZWlnaHRlZCBtZWFuIGZpdG5lc3MiKSArIGZhY2V0X3dyYXAofmNvbmRpdGlvbikgKyBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAuOCw3LjIpKSsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL2Jhcl9maXRuZXNzX2RhdGFfbnVtYmVyTXV0c19vbmx5Q29tYmlfb25seVNpZ25pZmljYW50LnBkZiIsIHAsIHdpZHRoPTE4LCBoZWlnaHQ9MTgsIHVuaXRzPSJjbSIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9iYXJfZml0bmVzc19kYXRhX251bWJlck11dHNfb25seUNvbWJpX29ubHlTaWduaWZpY2FudC5wbmciLCBwLCB3aWR0aD0xOCwgaGVpZ2h0PTE4LCB1bml0cz0iY20iKQpgYGAKCiMjIENvcnJlbGF0aW9uIHdpdGggZGlmZmVyZW50IHplcm8tc2hvdCBwcmVkaWN0b3JzIGFuZCBzaW1pbGFyCgpUaGVyZSBhcmUgZGlmZmVyZW50IHdheXMgb2YgcHJlZGljdGluZyB0aGUgZWZmZWN0IG9mIGhpZ2hlciBvcmRlciBtdXRhbnQgdmFyaWFudHMuIEZvciBkZXNpZ25pbmcgdGhlIGxpYnJhcnksIG5vIHByaW9yIGtub3dsZWRnZSB3YXMgYXZhaWxhYmxlLiBGb3IgdGhpcyByZWFzb24sIHplcm8tc2hvdCBwcmVkaWN0aW9ucyB1c2luZyBFVm11dGF0aW9uIGFuZCBEZWVwU2VxdWVuY2Ugd2VyZSBwZXJmb3JtZWQuIFdoZW4gZGF0YSBmb3Igc2luZ2xlLXNpdGUgbXV0YW50cyBpcyBhdmFpbGFibGUsIGFuIGFkZGl0aXZlIG1vZGVsIGNhbiBiZSB1c2VkIHRoYXQgaXMgYXNzdW1pbmcgaW5kZXBlbmRlbmNlIG9mIGRpZmZlcmVudCBhbWlubyBhY2lkIGV4Y2hhbmdlcy4gUHJlZGljdGlvbnMgb2YgdGhpcyBtb2RlbCB3ZXJlIGNhbGN1bGF0ZWQgdXNpbmcgcHl0aG9uLgoKYGBge3J9CmNvbWJpX3NhdF9hbHBoYSA8LSBjKGNvbWJpbmF0b3JpYWw9MC4yLCBzYXR1cmF0aW9uYWw9MC4yLCBXVD0xLjApCmNvbWJpX3NhdF9zaGFwZSA8LSBjKGNvbWJpbmF0b3JpYWw9MTYsIHNhdHVyYXRpb25hbD0xNiwgV1Q9NCkKY29tYmlfc2F0X3NpemUgPC0gYyhjb21iaW5hdG9yaWFsPTAuNCwgc2F0dXJhdGlvbmFsPTAuNCwgV1Q9MC44KQpkb3RzaXplIDwtIDAuMDIKcG9pbnRzaGFwZSA8LSAxNgphbHBoYWxldmVsIDwtIDAuNApsaW5lX3dpZHRoX2Zvcl9wbG90cyA8LSAwLjQKcmVkX2ZpdG5lc3MgPC0gdW5pcXVlKGZpdG5lc3NfZGF0YVtjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iLCAiY2F0ZWdvcnkiLCAiRVZjb3VwX3ByZWRpY3QiLCAiRGVlcFNlcV9wcmVkaWN0IiwgIk1TQV9UcmFuc2Zvcm0iLCAiYWRkaXRpdmVfc2NvcmUiLCAicHJvdGVpbk5QVF9wcmVkaWN0IildKQpyZWRfZml0bmVzc1tyZWRfZml0bmVzcyRjYXRlZ29yeT09ImNvbWJpQU5Ec2F0dXIiLF0kY2F0ZWdvcnkgPC0gInNhdHVyYXRpb25hbCIKcmVkX2ZpdG5lc3MgPC0gc3Vic2V0KHJlZF9maXRuZXNzLCByZWRfZml0bmVzcyRjYXRlZ29yeSAhPSAibm90RXhwZWN0ZWQiKQpyZWRfZml0bmVzc1tyZWRfZml0bmVzcyRjYXRlZ29yeT09IldUIixdJEVWY291cF9wcmVkaWN0IDwtIDAKYGBgCgpgYGB7cn0KcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxIDwtIHN1YnNldChyZWRfZml0bmVzcywgIWlzLm5hKHJlZF9maXRuZXNzJERlZXBTZXFfcHJlZGljdCkpCgpmb3IoY2F0IGluIGMoImNvbWJpbmF0b3JpYWwiLCAic2F0dXJhdGlvbmFsIikpewogIHByaW50KGNhdCkKICBzdWJzZXRfZm9yTG9vcCA8LSBzdWJzZXQocmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxLCByZWRfZml0bmVzc19ub05BX0RlZXBTZXEkY2F0ZWdvcnk9PWNhdCAmIHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSRjb25kaXRpb249PSJDTF9OMiIpCiAgbG0gPC0gbG0oRGVlcFNlcV9wcmVkaWN0fkVWY291cF9wcmVkaWN0LCBzdWJzZXRfZm9yTG9vcCkKICBjb3JyZWxhdGlvbiA8LSBjb3IudGVzdChzdWJzZXRfZm9yTG9vcCRFVmNvdXBfcHJlZGljdCwgc3Vic2V0X2Zvckxvb3AkRGVlcFNlcV9wcmVkaWN0LCBtZXRob2QgPSAnc3BlYXJtYW4nKQogIHByaW50KGNvcnJlbGF0aW9uKQogIGNvcnJlbGF0aW9uIDwtIGNvci50ZXN0KHN1YnNldF9mb3JMb29wJEVWY291cF9wcmVkaWN0LCBzdWJzZXRfZm9yTG9vcCREZWVwU2VxX3ByZWRpY3QsIG1ldGhvZCA9ICdwZWFyc29uJykKICBwcmludChjb3JyZWxhdGlvbikKfQoKc2NhdCA8LSBnZ3Bsb3QocmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxLCBhZXMoeD1FVmNvdXBfcHJlZGljdCwgeT1EZWVwU2VxX3ByZWRpY3QsIGNvbG9yPWNhdGVnb3J5LCBhbHBoYT1jYXRlZ29yeSwgbGluZXR5cGU9Y2F0ZWdvcnksIHNoYXBlPWNhdGVnb3J5LCBzaXplPWNhdGVnb3J5KSkgKyBnZW9tX3BvaW50KCkgKyBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9zaGFwZSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UsIGNvbG9yPSJibGFjayIsIGZvcm11bGEgPSB5IH4geCwgbGluZXdpZHRoPWxpbmVfd2lkdGhfZm9yX3Bsb3RzKSArIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXM9YygiY29tYmluYXRvcmlhbCI9ImRhc2hlZCIsICJzYXR1cmF0aW9uYWwiPSJ0d29kYXNoIikpICsgdGhlbWVfbGlnaHQoKSArIHhsYWIoIkVWY291cGxpbmdzIGZpdG5lc3MgcHJlZGljdGlvbiIpICsgeWxhYigiRGVlcFNlcXVlbmNlIGZpdG5lc3MgcHJlZGljdGlvbiIpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfYWxwaGEpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xfY29tYmlfc2F0KSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpzY2F0Cmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zY2F0dGVyX0RlZXBTZXFfRVZjb3VwLnBkZiIsIHNjYXQsIHdpZHRoPTIsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvc2NhdHRlcl9EZWVwU2VxX0VWY291cC5wbmciLCBzY2F0LCB3aWR0aD0yLCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKYGBgCgpgYGB7cn0KZm9yKGNhdCBpbiBjKCJjb21iaW5hdG9yaWFsIiwgInNhdHVyYXRpb25hbCIpKXsKICBwcmludChjYXQpCiAgc3Vic2V0X2Zvckxvb3AgPC0gc3Vic2V0KHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSwgcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxJGNhdGVnb3J5PT1jYXQgJiByZWRfZml0bmVzc19ub05BX0RlZXBTZXEkY29uZGl0aW9uPT0iQ0xfTjIiKQogIHByaW50KG5yb3coc3Vic2V0X2Zvckxvb3ApKQogIGNvcnJlbGF0aW9uIDwtIGNvci50ZXN0KHN1YnNldF9mb3JMb29wJEVWY291cF9wcmVkaWN0LCBzdWJzZXRfZm9yTG9vcCRNU0FfVHJhbnNmb3JtLCBtZXRob2QgPSAnc3BlYXJtYW4nKQogIHByaW50KGNvcnJlbGF0aW9uKQogIGNvcnJlbGF0aW9uIDwtIGNvci50ZXN0KHN1YnNldF9mb3JMb29wJEVWY291cF9wcmVkaWN0LCBzdWJzZXRfZm9yTG9vcCRNU0FfVHJhbnNmb3JtLCBtZXRob2QgPSAncGVhcnNvbicpCiAgcHJpbnQoY29ycmVsYXRpb24pCn0KCnNjYXQgPC0gZ2dwbG90KHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSwgYWVzKHg9RVZjb3VwX3ByZWRpY3QsIHk9TVNBX1RyYW5zZm9ybSwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5LCBsaW5ldHlwZT1jYXRlZ29yeSwgc2hhcGU9Y2F0ZWdvcnksIHNpemU9Y2F0ZWdvcnkpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NoYXBlKSArIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2l6ZSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSwgY29sb3I9ImJsYWNrIiwgZm9ybXVsYSA9IHkgfiB4LCBsaW5ld2lkdGg9bGluZV93aWR0aF9mb3JfcGxvdHMpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0iZGFzaGVkIiwgInNhdHVyYXRpb25hbCI9InR3b2Rhc2giKSkgKyB0aGVtZV9saWdodCgpICsgeGxhYigiRVZjb3VwbGluZ3MgZml0bmVzcyBwcmVkaWN0aW9uIikgKyB5bGFiKCJNU0EgVHJhbnNmb3JtIGZpdG5lc3MgcHJlZGljdGlvbiIpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfYWxwaGEpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xfY29tYmlfc2F0KSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpzY2F0Cmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zY2F0dGVyX01TQV9UcmFuc2Zvcm1fRVZjb3VwLnBkZiIsIHNjYXQsIHdpZHRoPTIsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvc2NhdHRlcl9NU0FfVHJhbnNmb3JtX0VWY291cC5wbmciLCBzY2F0LCB3aWR0aD0yLCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKYGBgCgpgYGB7cn0KcmVkX2ZpdG5lc3Nfbm9OQV9hZGRpdGl2ZSA8LSBzdWJzZXQocmVkX2ZpdG5lc3MsICFpcy5uYShyZWRfZml0bmVzcyRhZGRpdGl2ZV9zY29yZSkpCiMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTk2OTk4NTgvZ2dwbG90LWFkZGluZy1yZWdyZXNzaW9uLWxpbmUtZXF1YXRpb24tYW5kLXIyLXdpdGgtZmFjZXQKbG1fZXFuID0gZnVuY3Rpb24oZGYpewogICAgbSA9IGxtKGFkZGl0aXZlX3Njb3JlIH4gbm9ybSwgZGYpOwogICAgY29yX2Zvcm0gPSBjb3IudGVzdChkZiRub3JtLCBkZiRhZGRpdGl2ZV9zY29yZSwgbWV0aG9kID0gJ3BlYXJzb24nKQogICAgZXEgPC0gc3Vic3RpdHV0ZSgiciA9In5yLCAjaXRhbGljKHkpID09IGEgKyBiICUuJSBpdGFsaWMoeCkqIiByMj0ifnIyKiIgcD0gIn5wKiIsIFBlYXJzb24ncyByPSJ+ciAjUGVhcnNvbidzIAogICAgICAgICBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVtbMV1dLCBkaWdpdHMgPSAyKSwgCiAgICAgICAgICAgICAgYiA9IGZvcm1hdChjb2VmKG0pW1syXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgIHIyID0gZm9ybWF0KHN1bW1hcnkobSkkci5zcXVhcmVkLCBkaWdpdHMgPSAzKSwKICAgICAgICAgICAgIHIgPSBmb3JtYXQoY29yX2Zvcm0kZXN0aW1hdGVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgcCA9IGZvcm1hdChjb3JfZm9ybSRwLnZhbHVlW1sxXV0sIGRpZ2l0cz0yKSkpCiAgICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpOyAgICAgICAgICAgICAgICAgCn0KCmVxIDwtIGRkcGx5KHJlZF9maXRuZXNzX25vTkFfYWRkaXRpdmUsLihjb25kaXRpb24pLGxtX2VxbikKCnAgPC0gZ2dwbG90KGRhdGEgPSByZWRfZml0bmVzc19ub05BX2FkZGl0aXZlLCBhZXMoeCA9IG5vcm0sIHkgPSBhZGRpdGl2ZV9zY29yZSwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5KSkgKwogICAgICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplPWNhdGVnb3J5KSwgc2hhcGU9cG9pbnRzaGFwZSkgKwogICAgICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSwgY29sb3I9ImJsYWNrIiwgZm9ybXVsYSA9IHkgfiB4LGxpbmV0eXBlPSJkYXNoZWQiLCBsaW5ld2lkdGg9bGluZV93aWR0aF9mb3JfcGxvdHMpICsgeGxhYigiTm9ybWFsaXplZCBmaXRuZXNzIikgKyB5bGFiKCJBZGRpdGl2ZSBzY29yZSIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfYWxwaGEpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xfY29tYmlfc2F0KQpzY2F0ID0gcCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gLTAuNCwgeSA9IDEuOCxsYWJlbD1WMSksIHNpemU9OCwgc2l6ZS51bml0PSJwdCIsIHBhcnNlID0gVFJVRSwgaW5oZXJpdC5hZXM9RkFMU0UpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvc2NhdHRlcl9hZGRpdGl2ZV9jb21iaS5wZGYiLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvc2NhdHRlcl9hZGRpdGl2ZV9jb21iaS5wbmciLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpgYGAKCmBgYHtyfQpyZWRfZml0bmVzc19ub05BX2FkZGl0aXZlIDwtIHN1YnNldChyZWRfZml0bmVzcywgIWlzLm5hKHJlZF9maXRuZXNzJGFkZGl0aXZlX3Njb3JlKSkKIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xOTY5OTg1OC9nZ3Bsb3QtYWRkaW5nLXJlZ3Jlc3Npb24tbGluZS1lcXVhdGlvbi1hbmQtcjItd2l0aC1mYWNldApsbV9lcW4gPSBmdW5jdGlvbihkZil7CiAgICBtID0gbG0oYWRkaXRpdmVfc2NvcmUgfiBub3JtLCBkZik7CiAgICBjb3JfZm9ybSA9IGNvci50ZXN0KGRmJG5vcm0sIGRmJGFkZGl0aXZlX3Njb3JlLCBtZXRob2QgPSAnc3BlYXJtYW4nKQogICAgZXEgPC0gc3Vic3RpdHV0ZSgicmhvID0ifnIsICNpdGFsaWMoeSkgPT0gYSArIGIgJS4lIGl0YWxpYyh4KSoiIHIyPSJ+cjIqIiBwPSAifnAqIiwgUGVhcnNvbidzIHI9In5yICNQZWFyc29uJ3MgCiAgICAgICAgIGxpc3QoYSA9IGZvcm1hdChjb2VmKG0pW1sxXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgICBiID0gZm9ybWF0KGNvZWYobSlbWzJdXSwgZGlnaXRzID0gMiksIAogICAgICAgICAgICAgcjIgPSBmb3JtYXQoc3VtbWFyeShtKSRyLnNxdWFyZWQsIGRpZ2l0cyA9IDMpLAogICAgICAgICAgICAgciA9IGZvcm1hdChjb3JfZm9ybSRlc3RpbWF0ZVtbMV1dLCBkaWdpdHM9MiksCiAgICAgICAgICAgICBwID0gZm9ybWF0KGNvcl9mb3JtJHAudmFsdWVbWzFdXSwgZGlnaXRzPTIpKSkKICAgIGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSk7ICAgICAgICAgICAgICAgICAKfQoKZXEgPC0gZGRwbHkocmVkX2ZpdG5lc3Nfbm9OQV9hZGRpdGl2ZSwuKGNvbmRpdGlvbiksbG1fZXFuKQoKcCA8LSBnZ3Bsb3QoZGF0YSA9IHJlZF9maXRuZXNzX25vTkFfYWRkaXRpdmUsIGFlcyh4ID0gbm9ybSwgeSA9IGFkZGl0aXZlX3Njb3JlLCBjb2xvcj1jYXRlZ29yeSwgYWxwaGE9Y2F0ZWdvcnkpKSArCiAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHNpemU9Y2F0ZWdvcnkpLCBzaGFwZT1wb2ludHNoYXBlKSArCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsbGluZXR5cGU9ImRhc2hlZCIsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIkFkZGl0aXZlIHNjb3JlIikgKyB0aGVtZV9saWdodCgpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKSArIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2l6ZSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpCnNjYXQgPSBwICsgZ2VvbV90ZXh0KGRhdGE9ZXEsYWVzKHggPSAtMC40LCB5ID0gMS44LGxhYmVsPVYxKSwgc2l6ZT04LCBzaXplLnVuaXQ9InB0IiwgcGFyc2UgPSBUUlVFLCBpbmhlcml0LmFlcz1GQUxTRSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0Cmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9zY2F0dGVyX2FkZGl0aXZlX2NvbWJpX1NwZWFybWFuLnBkZiIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9zY2F0dGVyX2FkZGl0aXZlX2NvbWJpX1NwZWFybWFuLnBuZyIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmBgYAoKYGBge3J9CmxtX2VxbiA9IGZ1bmN0aW9uKGRmKXsKICAgIG0gPSBsbShEZWVwU2VxX3ByZWRpY3QgfiBub3JtLCBkZik7CiAgICBjb3JfZm9ybSA9IGNvci50ZXN0KGRmJG5vcm0sIGRmJERlZXBTZXFfcHJlZGljdCwgbWV0aG9kID0gJ3BlYXJzb24nKQogICAgZXEgPC0gc3Vic3RpdHV0ZSgiciA9In5yKn5jYXQyLCAjaXRhbGljKHkpID09IGEgKyBiICUuJSBpdGFsaWMoeCkqIiByMj0ifnIyKiIgcD0gIn5wKiIsIFBlYXJzb24ncyByPSJ+ciAjUGVhcnNvbidzIAogICAgICAgICBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVtbMV1dLCBkaWdpdHMgPSAyKSwgCiAgICAgICAgICAgICAgYiA9IGZvcm1hdChjb2VmKG0pW1syXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgIHIyID0gZm9ybWF0KHN1bW1hcnkobSkkci5zcXVhcmVkLCBkaWdpdHMgPSAzKSwKICAgICAgICAgICAgIHIgPSBmb3JtYXQoY29yX2Zvcm0kZXN0aW1hdGVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgcCA9IGZvcm1hdChjb3JfZm9ybSRwLnZhbHVlW1sxXV0sIGRpZ2l0cz0yKSwKICAgICAgICAgICAgIGNhdDIgPSB1bmlxdWUoZGYkY2F0ZWdvcnkyKSkpCiAgICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpOyAgICAgICAgICAgICAgICAgCn0KcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxJGNhdGVnb3J5MiA8LSByZWRfZml0bmVzc19ub05BX0RlZXBTZXEkY2F0ZWdvcnkKcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxW3JlZF9maXRuZXNzX25vTkFfRGVlcFNlcSRzZ1JOQV90YXJnZXQ9PSJXVCIsXSRjYXRlZ29yeTIgPC0gInNhdHVyYXRpb25hbCIKZXEgPC0gZGRwbHkocmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxLC4oY29uZGl0aW9uLCBjYXRlZ29yeTIpLGxtX2VxbikKCnJlZF9maXRuZXNzX25vTkFfRGVlcFNlcV9vbmx5Q29tYl9ubzQzMzcgPC0gc3Vic2V0KHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSwgcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxJGNhdGVnb3J5PT0iY29tYmluYXRvcmlhbCIgJiAhZ3JlcGwoIlYzMzciLCByZWRfZml0bmVzc19ub05BX0RlZXBTZXEkc2dSTkFfdGFyZ2V0KSkKY29ycmVsYXRpb24gPC0gY29yLnRlc3QocmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxX29ubHlDb21iX25vNDMzNyRub3JtLCByZWRfZml0bmVzc19ub05BX0RlZXBTZXFfb25seUNvbWJfbm80MzM3JERlZXBTZXFfcHJlZGljdCwgbWV0aG9kID0gJ3NwZWFybWFuJykKY29ycmVsYXRpb24KY29ycmVsYXRpb24gPC0gY29yLnRlc3QocmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxX29ubHlDb21iX25vNDMzNyRub3JtLCByZWRfZml0bmVzc19ub05BX0RlZXBTZXFfb25seUNvbWJfbm80MzM3JERlZXBTZXFfcHJlZGljdCwgbWV0aG9kID0gJ3BlYXJzb24nKQpjb3JyZWxhdGlvbgoKc2NhdCA8LSBnZ3Bsb3QoZGF0YSA9IHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSwgYWVzKHggPSBub3JtLCB5ID0gRGVlcFNlcV9wcmVkaWN0LCBjb2xvcj1jYXRlZ29yeSwgYWxwaGE9Y2F0ZWdvcnksIGxpbmV0eXBlPWNhdGVnb3J5Miwgc2hhcGU9Y2F0ZWdvcnkpKSArCiAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHNpemU9Y2F0ZWdvcnkpKSArIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2l6ZSkgKyBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9zaGFwZSkgKyAKICAgICAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UsIGNvbG9yPSJibGFjayIsIGZvcm11bGEgPSB5IH4geCwgbGluZXdpZHRoPWxpbmVfd2lkdGhfZm9yX3Bsb3RzKSArIHhsYWIoIk5vcm1hbGl6ZWQgZml0bmVzcyIpICsgeWxhYigiRGVlcFNlcXVlbmNlIHByZWRpY3Rpb24iKSArIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfYWxwaGEpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xfY29tYmlfc2F0KSArIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXM9YygiY29tYmluYXRvcmlhbCI9ImRhc2hlZCIsICJzYXR1cmF0aW9uYWwiPSJ0d29kYXNoIikpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdCA9IHNjYXQgKyBnZW9tX3RleHQoZGF0YT1lcSxhZXMoeCA9IDAuNSwgeSA9LTE1LCBsYWJlbD1WMSksIHZqdXN0PWMoMCwxLjIsMCwxLjIsMCwxLjIpLCBwYXJzZSA9IFRSVUUsIGluaGVyaXQuYWVzPUZBTFNFLCBzaXplPTgsIHNpemUudW5pdD0icHQiKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3NjYXR0ZXJfRGVlcFNlcV9ub3JtLnBkZiIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9zY2F0dGVyX0RlZXBTZXFfbm9ybS5wbmciLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpgYGAKCmBgYHtyfQpyZWRfZml0bmVzc19ub05BX0VWY291cCA8LSBzdWJzZXQocmVkX2ZpdG5lc3MsICFpcy5uYShyZWRfZml0bmVzcyRFVmNvdXBfcHJlZGljdCkpCgpsbV9lcW4gPSBmdW5jdGlvbihkZil7CiAgICBtID0gbG0oRVZjb3VwX3ByZWRpY3QgfiBub3JtLCBkZik7CiAgICBjb3JfZm9ybSA9IGNvci50ZXN0KGRmJG5vcm0sIGRmJEVWY291cF9wcmVkaWN0LCBtZXRob2QgPSAncGVhcnNvbicpCiAgICBlcSA8LSBzdWJzdGl0dXRlKCJyID0ifnIqfmNhdDIsICNpdGFsaWMoeSkgPT0gYSArIGIgJS4lIGl0YWxpYyh4KSoiIHIyPSJ+cjIqIiBwPSAifnAqIiwgUGVhcnNvbidzIHI9In5yICNQZWFyc29uJ3MgCiAgICAgICAgIGxpc3QoYSA9IGZvcm1hdChjb2VmKG0pW1sxXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgICBiID0gZm9ybWF0KGNvZWYobSlbWzJdXSwgZGlnaXRzID0gMiksIAogICAgICAgICAgICAgcjIgPSBmb3JtYXQoc3VtbWFyeShtKSRyLnNxdWFyZWQsIGRpZ2l0cyA9IDMpLAogICAgICAgICAgICAgciA9IGZvcm1hdChjb3JfZm9ybSRlc3RpbWF0ZVtbMV1dLCBkaWdpdHM9MiksCiAgICAgICAgICAgICBwID0gZm9ybWF0KGNvcl9mb3JtJHAudmFsdWVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgY2F0MiA9IHVuaXF1ZShkZiRjYXRlZ29yeTIpKSkKICAgIGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSk7ICAgICAgICAgICAgICAgICAKfQpyZWRfZml0bmVzc19ub05BX0VWY291cCRjYXRlZ29yeTIgPC0gcmVkX2ZpdG5lc3Nfbm9OQV9FVmNvdXAkY2F0ZWdvcnkKcmVkX2ZpdG5lc3Nfbm9OQV9FVmNvdXBbcmVkX2ZpdG5lc3Nfbm9OQV9FVmNvdXAkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kY2F0ZWdvcnkyIDwtICJzYXR1cmF0aW9uYWwiCmVxIDwtIGRkcGx5KHJlZF9maXRuZXNzX25vTkFfRVZjb3VwLC4oY29uZGl0aW9uLCBjYXRlZ29yeTIpLGxtX2VxbikKCnJlZF9maXRuZXNzX25vTkFfRVZjb3VwX25vNDMzNyA8LSBzdWJzZXQocmVkX2ZpdG5lc3Nfbm9OQV9FVmNvdXAsIHJlZF9maXRuZXNzX25vTkFfRVZjb3VwJGNhdGVnb3J5PT0iY29tYmluYXRvcmlhbCIgJiAhZ3JlcGwoIlYzMzciLCByZWRfZml0bmVzc19ub05BX0VWY291cCRzZ1JOQV90YXJnZXQpKQpjb3JyZWxhdGlvbiA8LSBjb3IudGVzdChyZWRfZml0bmVzc19ub05BX0VWY291cF9ubzQzMzckbm9ybSwgcmVkX2ZpdG5lc3Nfbm9OQV9FVmNvdXBfbm80MzM3JEVWY291cF9wcmVkaWN0LCBtZXRob2QgPSAnc3BlYXJtYW4nKQpjb3JyZWxhdGlvbgpjb3JyZWxhdGlvbiA8LSBjb3IudGVzdChyZWRfZml0bmVzc19ub05BX0VWY291cF9ubzQzMzckbm9ybSwgcmVkX2ZpdG5lc3Nfbm9OQV9FVmNvdXBfbm80MzM3JEVWY291cF9wcmVkaWN0LCBtZXRob2QgPSAncGVhcnNvbicpCmNvcnJlbGF0aW9uCgpzY2F0IDwtIGdncGxvdChkYXRhID0gcmVkX2ZpdG5lc3Nfbm9OQV9FVmNvdXAsIGFlcyh4ID0gbm9ybSwgeSA9IEVWY291cF9wcmVkaWN0LCBjb2xvcj1jYXRlZ29yeSwgYWxwaGE9Y2F0ZWdvcnksIGxpbmV0eXBlPWNhdGVnb3J5Miwgc2hhcGU9Y2F0ZWdvcnkpKSArCiAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHNpemU9Y2F0ZWdvcnkpKSArIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2l6ZSkgKyBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9zaGFwZSkgKwogICAgICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSwgY29sb3I9ImJsYWNrIiwgZm9ybXVsYSA9IHkgfiB4LCBsaW5ld2lkdGg9bGluZV93aWR0aF9mb3JfcGxvdHMpICsgeGxhYigiTm9ybWFsaXplZCBmaXRuZXNzIikgKyB5bGFiKCJFVm11dGF0aW9uIHByZWRpY3Rpb24iKSArIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfYWxwaGEpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xfY29tYmlfc2F0KSArIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXM9YygiY29tYmluYXRvcmlhbCI9ImRhc2hlZCIsICJzYXR1cmF0aW9uYWwiPSJ0d29kYXNoIikpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdCA9IHNjYXQgKyBnZW9tX3RleHQoZGF0YT1lcSxhZXMoeCA9IDAuNSwgeSA9LTEwLCBsYWJlbD1WMSksIHZqdXN0PWMoMCwxLjIsMCwxLjIsMCwxLjIpLCBwYXJzZSA9IFRSVUUsIGluaGVyaXQuYWVzPUZBTFNFLCBzaXplPTgsIHNpemUudW5pdCA9ICJwdCIpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvc2NhdHRlcl9ub3JtX2FsbF9FVmNvdXAucGRmIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3NjYXR0ZXJfbm9ybV9hbGxfRVZjb3VwLnBuZyIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmBgYAoKYGBge3J9CmxtX2VxbiA9IGZ1bmN0aW9uKGRmKXsKICAgIG0gPSBsbShNU0FfVHJhbnNmb3JtIH4gbm9ybSwgZGYpOwogICAgY29yX2Zvcm0gPSBjb3IudGVzdChkZiRub3JtLCBkZiRNU0FfVHJhbnNmb3JtLCBtZXRob2QgPSAncGVhcnNvbicpCiAgICBlcSA8LSBzdWJzdGl0dXRlKCJyID0ifnIqfmNhdDIsICNpdGFsaWMoeSkgPT0gYSArIGIgJS4lIGl0YWxpYyh4KSoiIHIyPSJ+cjIqIiBwPSAifnAqIiwgUGVhcnNvbidzIHI9In5yICNQZWFyc29uJ3MgCiAgICAgICAgIGxpc3QoYSA9IGZvcm1hdChjb2VmKG0pW1sxXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgICBiID0gZm9ybWF0KGNvZWYobSlbWzJdXSwgZGlnaXRzID0gMiksIAogICAgICAgICAgICAgcjIgPSBmb3JtYXQoc3VtbWFyeShtKSRyLnNxdWFyZWQsIGRpZ2l0cyA9IDMpLAogICAgICAgICAgICAgciA9IGZvcm1hdChjb3JfZm9ybSRlc3RpbWF0ZVtbMV1dLCBkaWdpdHM9MiksCiAgICAgICAgICAgICBwID0gZm9ybWF0KGNvcl9mb3JtJHAudmFsdWVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgY2F0MiA9IHVuaXF1ZShkZiRjYXRlZ29yeTIpKSkKICAgIGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSk7ICAgICAgICAgICAgICAgICAKfQpyZWRfZml0bmVzc19ub05BX0RlZXBTZXEkY2F0ZWdvcnkyIDwtIHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSRjYXRlZ29yeQpyZWRfZml0bmVzc19ub05BX0RlZXBTZXFbcmVkX2ZpdG5lc3Nfbm9OQV9EZWVwU2VxJHNnUk5BX3RhcmdldD09IldUIixdJGNhdGVnb3J5MiA8LSAic2F0dXJhdGlvbmFsIgplcSA8LSBkZHBseShyZWRfZml0bmVzc19ub05BX0RlZXBTZXEsLihjb25kaXRpb24sIGNhdGVnb3J5MiksbG1fZXFuKQoKc2NhdCA8LSBnZ3Bsb3QoZGF0YSA9IHJlZF9maXRuZXNzX25vTkFfRGVlcFNlcSwgYWVzKHggPSBub3JtLCB5ID0gTVNBX1RyYW5zZm9ybSwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5LCBsaW5ldHlwZT1jYXRlZ29yeTIsIHNoYXBlPWNhdGVnb3J5KSkgKwogICAgICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplPWNhdGVnb3J5KSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2hhcGUpICsgCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIk1TQVRyYW5zZm9ybWVyIChlbnNlbWJsZSkgcHJlZGljdGlvbiIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0iZGFzaGVkIiwgInNhdHVyYXRpb25hbCI9InR3b2Rhc2giKSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0ID0gc2NhdCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gMC41LCB5ID0tMTAsIGxhYmVsPVYxKSwgc2l6ZT04LCBzaXplLnVuaXQ9InB0Iiwgdmp1c3Q9YygwLDEuMiwwLDEuMiwwLDEuMiksIHBhcnNlID0gVFJVRSwgaW5oZXJpdC5hZXM9RkFMU0UpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvc2NhdHRlcl9NU0FfVHJhbnNmb3JtX25vcm0ucGRmIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3NjYXR0ZXJfTVNBX1RyYW5zZm9ybV9ub3JtLnBuZyIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmBgYAoKYGBge3J9CmRmX3Byb3RlaW5OUFQgPC0gc3Vic2V0KHJlZF9maXRuZXNzLCAhaXMubmEocmVkX2ZpdG5lc3MkcHJvdGVpbk5QVF9wcmVkaWN0KSkKCmxtX2VxbiA9IGZ1bmN0aW9uKGRmKXsKICAgIG0gPSBsbShwcm90ZWluTlBUX3ByZWRpY3QgfiBub3JtLCBkZik7CiAgICBjb3JfZm9ybSA9IGNvci50ZXN0KGRmJG5vcm0sIGRmJHByb3RlaW5OUFRfcHJlZGljdCwgbWV0aG9kID0gJ3BlYXJzb24nKQogICAgZXEgPC0gc3Vic3RpdHV0ZSgiciA9In5yKn5jYXQyLCAjaXRhbGljKHkpID09IGEgKyBiICUuJSBpdGFsaWMoeCkqIiByMj0ifnIyKiIgcD0gIn5wKiIsIFBlYXJzb24ncyByPSJ+ciAjUGVhcnNvbidzIAogICAgICAgICBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVtbMV1dLCBkaWdpdHMgPSAyKSwgCiAgICAgICAgICAgICAgYiA9IGZvcm1hdChjb2VmKG0pW1syXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgIHIyID0gZm9ybWF0KHN1bW1hcnkobSkkci5zcXVhcmVkLCBkaWdpdHMgPSAzKSwKICAgICAgICAgICAgIHIgPSBmb3JtYXQoY29yX2Zvcm0kZXN0aW1hdGVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgcCA9IGZvcm1hdChjb3JfZm9ybSRwLnZhbHVlW1sxXV0sIGRpZ2l0cz0yKSwKICAgICAgICAgICAgIGNhdDIgPSB1bmlxdWUoZGYkY2F0ZWdvcnkyKSkpCiAgICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpOyAgICAgICAgICAgICAgICAgCn0KZGZfcHJvdGVpbk5QVCRjYXRlZ29yeTIgPC0gZGZfcHJvdGVpbk5QVCRjYXRlZ29yeQpkZl9wcm90ZWluTlBUW2RmX3Byb3RlaW5OUFQkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kY2F0ZWdvcnkyIDwtICJzYXR1cmF0aW9uYWwiCmVxIDwtIGRkcGx5KGRmX3Byb3RlaW5OUFQsLihjb25kaXRpb24sIGNhdGVnb3J5MiksbG1fZXFuKQoKc2NhdCA8LSBnZ3Bsb3QoZGF0YSA9IGRmX3Byb3RlaW5OUFQsIGFlcyh4ID0gbm9ybSwgeSA9IHByb3RlaW5OUFRfcHJlZGljdCwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5LCBsaW5ldHlwZT1jYXRlZ29yeTIsIHNoYXBlPWNhdGVnb3J5KSkgKwogICAgICAgICAgICBnZW9tX3BvaW50KGFlcyhzaXplPWNhdGVnb3J5KSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NpemUpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2hhcGUpICsgCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIlByb3RlaW5OUFQgcHJlZGljdGlvbiIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0iZGFzaGVkIiwgInNhdHVyYXRpb25hbCI9InR3b2Rhc2giKSkgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0ID0gc2NhdCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gMC41LCB5ID0tMiwgbGFiZWw9VjEpLCBzaXplPTgsIHNpemUudW5pdD0icHQiLCBwYXJzZSA9IFRSVUUsIGluaGVyaXQuYWVzPUZBTFNFKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3NjYXR0ZXJfcHJvdGVpbk5QVF9ub3JtLnBkZiIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9zY2F0dGVyX3Byb3RlaW5OUFRfbm9ybS5wbmciLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQoKbG1fZXFuID0gZnVuY3Rpb24oZGYpewogICAgbSA9IGxtKHByb3RlaW5OUFRfcHJlZGljdCB+IG5vcm0sIGRmKTsKICAgIGNvcl9mb3JtID0gY29yLnRlc3QoZGYkbm9ybSwgZGYkcHJvdGVpbk5QVF9wcmVkaWN0LCBtZXRob2QgPSAnc3BlYXJtYW4nKQogICAgZXEgPC0gc3Vic3RpdHV0ZSgicmhvID0ifnIqfmNhdDIsICNpdGFsaWMoeSkgPT0gYSArIGIgJS4lIGl0YWxpYyh4KSoiIHIyPSJ+cjIqIiBwPSAifnAqIiwgUGVhcnNvbidzIHI9In5yICNQZWFyc29uJ3MgCiAgICAgICAgIGxpc3QoYSA9IGZvcm1hdChjb2VmKG0pW1sxXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgICBiID0gZm9ybWF0KGNvZWYobSlbWzJdXSwgZGlnaXRzID0gMiksIAogICAgICAgICAgICAgcjIgPSBmb3JtYXQoc3VtbWFyeShtKSRyLnNxdWFyZWQsIGRpZ2l0cyA9IDMpLAogICAgICAgICAgICAgciA9IGZvcm1hdChjb3JfZm9ybSRlc3RpbWF0ZVtbMV1dLCBkaWdpdHM9MiksCiAgICAgICAgICAgICBwID0gZm9ybWF0KGNvcl9mb3JtJHAudmFsdWVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgY2F0MiA9IHVuaXF1ZShkZiRjYXRlZ29yeTIpKSkKICAgIGFzLmNoYXJhY3Rlcihhcy5leHByZXNzaW9uKGVxKSk7ICAgICAgICAgICAgICAgICAKfQpkZl9wcm90ZWluTlBUJGNhdGVnb3J5MiA8LSBkZl9wcm90ZWluTlBUJGNhdGVnb3J5CmRmX3Byb3RlaW5OUFRbZGZfcHJvdGVpbk5QVCRzZ1JOQV90YXJnZXQ9PSJXVCIsXSRjYXRlZ29yeTIgPC0gInNhdHVyYXRpb25hbCIKZXEgPC0gZGRwbHkoZGZfcHJvdGVpbk5QVCwuKGNvbmRpdGlvbiwgY2F0ZWdvcnkyKSxsbV9lcW4pCgpzY2F0IDwtIGdncGxvdChkYXRhID0gZGZfcHJvdGVpbk5QVCwgYWVzKHggPSBub3JtLCB5ID0gcHJvdGVpbk5QVF9wcmVkaWN0LCBjb2xvcj1jYXRlZ29yeSwgYWxwaGE9Y2F0ZWdvcnksIGxpbmV0eXBlPWNhdGVnb3J5Miwgc2hhcGU9Y2F0ZWdvcnkpKSArCiAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHNpemU9Y2F0ZWdvcnkpKSArIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2l6ZSkgKyBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9zaGFwZSkgKyAKICAgICAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UsIGNvbG9yPSJibGFjayIsIGZvcm11bGEgPSB5IH4geCwgbGluZXdpZHRoPWxpbmVfd2lkdGhfZm9yX3Bsb3RzKSArIHhsYWIoIk5vcm1hbGl6ZWQgZml0bmVzcyIpICsgeWxhYigiUHJvdGVpbk5QVCBwcmVkaWN0aW9uIikgKyB0aGVtZV9saWdodCgpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKSArIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X2FscGhhKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9Y29sX2NvbWJpX3NhdCkgKyBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzPWMoImNvbWJpbmF0b3JpYWwiPSJkYXNoZWQiLCAic2F0dXJhdGlvbmFsIj0idHdvZGFzaCIpKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQgPSBzY2F0ICsgZ2VvbV90ZXh0KGRhdGE9ZXEsYWVzKHggPSAwLjUsIHkgPS0yLCBsYWJlbD1WMSksIHNpemU9OCwgc2l6ZS51bml0PSJwdCIsIHBhcnNlID0gVFJVRSwgaW5oZXJpdC5hZXM9RkFMU0UpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvc2NhdHRlcl9wcm90ZWluTlBUX25vcm1fc3BlYXJtYW4ucGRmIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3NjYXR0ZXJfcHJvdGVpbk5QVF9ub3JtX3NwZWFybWFuLnBuZyIsIHNjYXQsIHdpZHRoPTYuOCwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmBgYAoKCmBgYHtyfQpzY2F0IDwtIGdncGxvdChkZl9wcm90ZWluTlBULCBhZXMoeD1wcm90ZWluTlBUX3ByZWRpY3QsIHk9TVNBX1RyYW5zZm9ybSwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5LCBsaW5ldHlwZT1jYXRlZ29yeSwgc2hhcGU9Y2F0ZWdvcnksIHNpemU9Y2F0ZWdvcnkpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NoYXBlKSArIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2l6ZSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSwgY29sb3I9ImJsYWNrIiwgZm9ybXVsYSA9IHkgfiB4LCBsaW5ld2lkdGg9bGluZV93aWR0aF9mb3JfcGxvdHMpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0iZGFzaGVkIiwgInNhdHVyYXRpb25hbCI9InR3b2Rhc2giKSkgKyB0aGVtZV9saWdodCgpICsgeGxhYigiUHJvdGVpbk5QVCBmaXRuZXNzIHByZWRpY3Rpb24iKSArIHlsYWIoIk1TQSBUcmFuc2Zvcm0gZml0bmVzcyBwcmVkaWN0aW9uIikgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9hbHBoYSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWNvbF9jb21iaV9zYXQpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvc2NhdHRlcl9NU0FfVHJhbnNmb3JtX3Byb3RlaW5OUFQucGRmIiwgc2NhdCwgd2lkdGg9MiwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9zY2F0dGVyX01TQV9UcmFuc2Zvcm1fcHJvdGVpbk5QVC5wbmciLCBzY2F0LCB3aWR0aD0yLCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKCnNjYXQgPC0gZ2dwbG90KGRmX3Byb3RlaW5OUFQsIGFlcyh4PXByb3RlaW5OUFRfcHJlZGljdCwgeT1hZGRpdGl2ZV9zY29yZSwgY29sb3I9Y2F0ZWdvcnksIGFscGhhPWNhdGVnb3J5LCBsaW5ldHlwZT1jYXRlZ29yeSwgc2hhcGU9Y2F0ZWdvcnksIHNpemU9Y2F0ZWdvcnkpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NoYXBlKSArIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfc2l6ZSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSwgY29sb3I9ImJsYWNrIiwgZm9ybXVsYSA9IHkgfiB4LCBsaW5ld2lkdGg9bGluZV93aWR0aF9mb3JfcGxvdHMpICsgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcz1jKCJjb21iaW5hdG9yaWFsIj0iZGFzaGVkIiwgInNhdHVyYXRpb25hbCI9InR3b2Rhc2giKSkgKyB0aGVtZV9saWdodCgpICsgeGxhYigiUHJvdGVpbk5QVCBmaXRuZXNzIHByZWRpY3Rpb24iKSArIHlsYWIoIkFkZGl0aXZlIHNjb3JlIGZpdG5lc3MgcHJlZGljdGlvbiIpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfYWxwaGEpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xfY29tYmlfc2F0KSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3NjYXR0ZXJfYWRkaXRpdmVTY29yZV9wcm90ZWluTlBULnBkZiIsIHNjYXQsIHdpZHRoPTIsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvc2NhdHRlcl9hZGRpdGl2ZVNjb3JlX3Byb3RlaW5OUFQucG5nIiwgc2NhdCwgd2lkdGg9MiwgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmBgYAoKIyMgQ29ycmVsYXRpb24gY29uc2VydmF0aW9uIHRvIGZpdG5lc3MKCmBgYHtyfQpjb3Jfc3VicyA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkY2F0ZWdvcnk9PSJzYXR1cmF0aW9uYWwiIHwgZml0bmVzc19kYXRhJGNhdGVnb3J5PT0iY29tYmlBTkRzYXR1ciIpCmNvcl9zdWJzW2Nvcl9zdWJzJGNhdGVnb3J5PT0iY29tYmlBTkRzYXR1ciIsXSRjYXRlZ29yeSA8LSAic2F0dXJhdGlvbmFsIgpjb3Jfc3VicyA8LSBzdWJzZXQoY29yX3N1YnMsICFpcy5uYShjb3Jfc3VicyRjb25zZXJ2YXRpb25TY29yZSkpCgpsbV9lcW4gPSBmdW5jdGlvbihkZil7CiAgICBtID0gbG0oY29uc2VydmF0aW9uU2NvcmUgfiBub3JtLCBkZik7CiAgICBjb3JfZm9ybSA9IGNvci50ZXN0KGRmJG5vcm0sIGRmJGNvbnNlcnZhdGlvblNjb3JlLCBtZXRob2QgPSAncGVhcnNvbicpCiAgICBlcSA8LSBzdWJzdGl0dXRlKCJQZWFyc29uJ3MgciA9In5yLCAjaXRhbGljKHkpID09IGEgKyBiICUuJSBpdGFsaWMoeCkqIiByMj0ifnIyKiIgcD0gIn5wKiIsIFBlYXJzb24ncyByPSJ+cgogICAgICAgICBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVtbMV1dLCBkaWdpdHMgPSAyKSwgCiAgICAgICAgICAgICAgYiA9IGZvcm1hdChjb2VmKG0pW1syXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgIHIyID0gZm9ybWF0KHN1bW1hcnkobSkkci5zcXVhcmVkLCBkaWdpdHMgPSAzKSwKICAgICAgICAgICAgIHIgPSBmb3JtYXQoY29yX2Zvcm0kZXN0aW1hdGVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgcCA9IGZvcm1hdChjb3JfZm9ybSRwLnZhbHVlW1sxXV0sIGRpZ2l0cz0yKSkpCiAgICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpOyAgICAgICAgICAgICAgICAgCn0KZXEgPC0gZGRwbHkoY29yX3N1YnMsLihjb25kaXRpb24pLGxtX2VxbikKCnNjYXQgPC0gZ2dwbG90KGRhdGEgPSBjb3Jfc3VicywgYWVzKHggPSBub3JtLCB5ID0gY29uc2VydmF0aW9uU2NvcmUsIGNvbG9yPWNhdGVnb3J5LCBhbHBoYT1jYXRlZ29yeSwgbGluZXR5cGU9Y2F0ZWdvcnkpKSArCiAgICAgICAgICAgIGdlb21fcG9pbnQoYWVzKHNpemU9Y2F0ZWdvcnksIHNoYXBlPWNhdGVnb3J5KSkgICsgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzPWNvbWJpX3NhdF9zaXplKSArIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9Y29tYmlfc2F0X3NoYXBlKSArIAogICAgICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSwgY29sb3I9ImJsYWNrIiwgZm9ybXVsYSA9IHkgfiB4LCBsaW5ld2lkdGg9bGluZV93aWR0aF9mb3JfcGxvdHMpICsgeGxhYigiTm9ybWFsaXplZCBmaXRuZXNzIikgKyB5bGFiKCJDb25zZXJ2YXRpb24gc2NvcmUiKSArIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1jb21iaV9zYXRfYWxwaGEpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jb2xfY29tYmlfc2F0KSArIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXM9YygiY29tYmluYXRvcmlhbCI9ImRhc2hlZCIsICJzYXR1cmF0aW9uYWwiPSJ0d29kYXNoIikpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdCA9IHNjYXQgKyBnZW9tX3RleHQoZGF0YT1lcSxhZXMoeCA9IDEsIHkgPTAuOSwgbGFiZWw9VjEpLCBzaXplPTgsIHNpemUudW5pdD0icHQiLCBwYXJzZSA9IFRSVUUsIGluaGVyaXQuYWVzPUZBTFNFKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQKCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9jb25zZXJ2YXRpb25fbm9ybS5wZGYiLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvY29uc2VydmF0aW9uX25vcm0ucG5nIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKYGBgCgojIyBDb3JyZWxhdGlvbiBzdXJmYWNlIGV4cG9zdXJlIHRvIGZpdG5lc3MKCmBgYHtyfQpzdXJmYWNlIDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRjYXRlZ29yeT09InNhdHVyYXRpb25hbCIgfCBmaXRuZXNzX2RhdGEkY2F0ZWdvcnk9PSJjb21iaUFORHNhdHVyIikKc3VyZmFjZVtzdXJmYWNlJGNhdGVnb3J5PT0iY29tYmlBTkRzYXR1ciIsXSRjYXRlZ29yeSA8LSAic2F0dXJhdGlvbmFsIgpzdXJmYWNlIDwtIHN1YnNldChzdXJmYWNlLCAhaXMubmEoc3VyZmFjZSRyZWxTQVMpKQoKbG1fZXFuID0gZnVuY3Rpb24oZGYpewogICAgbSA9IGxtKHJlbFNBUyB+IG5vcm0sIGRmKTsKICAgIGNvcl9mb3JtID0gY29yLnRlc3QoZGYkbm9ybSwgZGYkcmVsU0FTLCBtZXRob2QgPSAncGVhcnNvbicpCiAgICBlcSA8LSBzdWJzdGl0dXRlKCJQZWFyc29uJ3MgciA9In5yLCAjaXRhbGljKHkpID09IGEgKyBiICUuJSBpdGFsaWMoeCkqIiByMj0ifnIyKiIgcD0gIn5wKiIsIFBlYXJzb24ncyByPSJ+cgogICAgICAgICBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVtbMV1dLCBkaWdpdHMgPSAyKSwgCiAgICAgICAgICAgICAgYiA9IGZvcm1hdChjb2VmKG0pW1syXV0sIGRpZ2l0cyA9IDIpLCAKICAgICAgICAgICAgIHIyID0gZm9ybWF0KHN1bW1hcnkobSkkci5zcXVhcmVkLCBkaWdpdHMgPSAzKSwKICAgICAgICAgICAgIHIgPSBmb3JtYXQoY29yX2Zvcm0kZXN0aW1hdGVbWzFdXSwgZGlnaXRzPTIpLAogICAgICAgICAgICAgcCA9IGZvcm1hdChjb3JfZm9ybSRwLnZhbHVlW1sxXV0sIGRpZ2l0cz0yKSkpCiAgICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkpOyAgICAgICAgICAgICAgICAgCn0KZXEgPC0gZGRwbHkoc3VyZmFjZSwuKGNvbmRpdGlvbiksbG1fZXFuKQoKc2NhdCA8LSBnZ3Bsb3QoZGF0YSA9IHN1cmZhY2UsIGFlcyh4ID0gbm9ybSwgeSA9IHJlbFNBUywgY29sb3I9ZGltZXIpKSArCiAgICAgICAgICAgIGdlb21fcG9pbnQoc2l6ZT1kb3RzaXplLCBzaGFwZT1wb2ludHNoYXBlLCBhbHBoYT1hbHBoYWxldmVsKSArCiAgICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlPUZBTFNFLCBjb2xvcj0iYmxhY2siLCBmb3JtdWxhID0geSB+IHgsIGxpbmV0eXBlPSJkYXNoZWQiLCBsaW5ld2lkdGg9bGluZV93aWR0aF9mb3JfcGxvdHMpICsgeGxhYigiTm9ybWFsaXplZCBmaXRuZXNzIikgKyB5bGFiKCJSZWxhdGl2ZSBzb2x2ZW50IGFjY2Vzc2liaWxpdHkiKSArIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZShwYW5lbC5ncmlkLm1pbm9yID1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCIjOTNkZmZmZmYiKSwgbmEudmFsdWU9IiM3Nzc3NzciKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQgPSBzY2F0ICsgZ2VvbV90ZXh0KGRhdGE9ZXEsYWVzKHggPSAxLjAsIHkgPS0wLjA1LCBsYWJlbD1WMSksIHNpemU9OCwgc2l6ZS51bml0PSJwdCIsIHBhcnNlID0gVFJVRSwgaW5oZXJpdC5hZXM9RkFMU0UpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvcmVsU0FTX25vcm0ucGRmIiwgc2NhdCwgd2lkdGg9NywgaGVpZ2h0PTIsIHVuaXRzPSJpbiIpCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9yZWxTQVNfbm9ybS5wbmciLCBzY2F0LCB3aWR0aD0zMCwgaGVpZ2h0PTE4LCB1bml0cz0iY20iKQoKCnN1cmZhY2Vfbm9EaW1lciA8LSBzdWJzZXQoc3VyZmFjZSwgaXMubmEoc3VyZmFjZSRkaW1lcikpCmVxIDwtIGRkcGx5KHN1cmZhY2Vfbm9EaW1lciwuKGNvbmRpdGlvbiksbG1fZXFuKQoKc2NhdCA8LSBnZ3Bsb3QoZGF0YSA9IHN1cmZhY2Vfbm9EaW1lciwgYWVzKHggPSBub3JtLCB5ID0gcmVsU0FTLCBjb2xvcj1kaW1lcikpICsKICAgICAgICAgICAgZ2VvbV9wb2ludChzaXplPWRvdHNpemUsIHNoYXBlPXBvaW50c2hhcGUsIGFscGhhPWFscGhhbGV2ZWwpICsKICAgICAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2U9RkFMU0UsIGNvbG9yPSJibGFjayIsIGZvcm11bGEgPSB5IH4geCwgbGluZXR5cGU9ImRhc2hlZCIsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyB4bGFiKCJOb3JtYWxpemVkIGZpdG5lc3MiKSArIHlsYWIoIlJlbGF0aXZlIHNvbHZlbnQgYWNjZXNzaWJpbGl0eSIpICsgdGhlbWVfbGlnaHQoKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPWVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBzdHJpcC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsID0gTkEsIGNvbG9yID0gIndoaXRlIiksIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIiwgaGp1c3QgPSAwKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLCBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTgpLCBsZWdlbmQudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoIiM5M2RmZmZmZiIpLCBuYS52YWx1ZT0iIzc3Nzc3NyIpICsgZmFjZXRfd3JhcChjb25kaXRpb25+LikKc2NhdCA9IHNjYXQgKyBnZW9tX3RleHQoZGF0YT1lcSxhZXMoeCA9IDEuMCwgeSA9LTAuMDUsIGxhYmVsPVYxKSwgc2l6ZT04LCBzaXplLnVuaXQgPSAicHQiLCBwYXJzZSA9IFRSVUUsIGluaGVyaXQuYWVzPUZBTFNFKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3JlbFNBU19ub3JtX25vRGltZXIucGRmIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3JlbFNBU19ub3JtX25vRGltZXIucG5nIiwgc2NhdCwgd2lkdGg9Ni44LCBoZWlnaHQ9MiwgdW5pdHM9ImluIikKCgpzdXJmYWNlX0RpbWVyIDwtIHN1YnNldChzdXJmYWNlLCAhaXMubmEoc3VyZmFjZSRkaW1lcikpCmVxIDwtIGRkcGx5KHN1cmZhY2VfRGltZXIsLihjb25kaXRpb24pLGxtX2VxbikKCnNjYXQgPC0gZ2dwbG90KGRhdGEgPSBzdXJmYWNlX0RpbWVyLCBhZXMoeCA9IG5vcm0sIHkgPSByZWxTQVMsIGNvbG9yPWRpbWVyKSkgKwogICAgICAgICAgICBnZW9tX3BvaW50KHNpemU9ZG90c2l6ZSwgc2hhcGU9cG9pbnRzaGFwZSwgYWxwaGE9YWxwaGFsZXZlbCkgKwogICAgICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZT1GQUxTRSwgY29sb3I9ImJsYWNrIiwgZm9ybXVsYSA9IHkgfiB4LCBsaW5ldHlwZT0iZGFzaGVkIiwgbGluZXdpZHRoPWxpbmVfd2lkdGhfZm9yX3Bsb3RzKSArIHhsYWIoIk5vcm1hbGl6ZWQgZml0bmVzcyIpICsgeWxhYigiUmVsYXRpdmUgc29sdmVudCBhY2Nlc3NpYmlsaXR5IikgKyB0aGVtZV9saWdodCgpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9ZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiIzkzZGZmZmZmIiksIG5hLnZhbHVlPSIjNzc3Nzc3IikgKyBmYWNldF93cmFwKGNvbmRpdGlvbn4uKQpzY2F0ID0gc2NhdCArIGdlb21fdGV4dChkYXRhPWVxLGFlcyh4ID0gMS4wLCB5ID0tMC4wNSwgbGFiZWw9VjEpLCBzaXplPTgsIHNpemUudW5pdD0icHQiLCBwYXJzZSA9IFRSVUUsIGluaGVyaXQuYWVzPUZBTFNFKSArIGZhY2V0X3dyYXAoY29uZGl0aW9ufi4pCnNjYXQKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3JlbFNBU19ub3JtX29ubHlEaW1lci5wZGYiLCBzY2F0LCB3aWR0aD02LjgsIGhlaWdodD0yLCB1bml0cz0iaW4iKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvcmVsU0FTX25vcm1fb25seURpbWVyLnBuZyIsIHNjYXQsIHdpZHRoPTMwLCBoZWlnaHQ9MTgsIHVuaXRzPSJjbSIpCmBgYAoKIyBDb21wYXJpbmcgZGlmZmVyZW50IGNvbmRpdGlvbnMKCkEgY29tcGFyaXNvbiBiZXR3ZWVuIGRpZmZlcmVudCBjb25kaXRpb25zIGhlbHBzIHRvIGlkZW50aWZ5IHZhcmlhbnRzIHdoaWNoIGJlaGF2ZSBkaWZmZXJlbnQgaW4gZGlmZmVyZW50IGNvbmRpdGlvbnMsIGUuZy4gYmVjYXVzZSBvZiBhbiBlbmhhbmNlZCBzcGVjaWZpY2l0eSBvciBoaWdoZXIgY29tcGF0aWJpbGl0eSB3aXRoIGxpZ2h0LWRhcmsgY3ljbGVzLgoKIyMgQ29tcGFyZSBOMiBhbmQgTzIgZ2FzIGZlZWRzCgpgYGB7ciBmaXRuZXNzLWZpdG5lc3MtcGxvdHMtTjItTzJ9CnJlZF9maXRuZXNzIDwtIHVuaXF1ZShmaXRuZXNzX2RhdGFbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJwX2ZpdF9hZGpfV1QiLCAibnVtX2JhcmNvZGVzIiwgImNhdGVnb3J5IildKQp3aWRlX2ZpdG5lc3MgPC0gcGl2b3Rfd2lkZXIocmVkX2ZpdG5lc3MsIHZhbHVlc19mcm9tID1jKG5vcm0scF9maXRfYWRqX1dUKSwgbmFtZXNfZnJvbT1jb25kaXRpb24pCndpZGVfZml0bmVzcyRzZ1JOQV90YXJnZXQgPC0gZmFjdG9yKHdpZGVfZml0bmVzcyRzZ1JOQV90YXJnZXQsIGxldmVscz1jKHVuaXF1ZShzdWJzZXQod2lkZV9maXRuZXNzLCB3aWRlX2ZpdG5lc3Mkc2dSTkFfdGFyZ2V0ICE9ICJXVCIpKSRzZ1JOQV90YXJnZXQsICJXVCIpKQp3aWRlX2ZpdG5lc3MkV1Rfbm90X1dUIDwtICJub3RXVCIKd2lkZV9maXRuZXNzW3dpZGVfZml0bmVzcyRzZ1JOQV90YXJnZXQ9PSJXVCIsXSRXVF9ub3RfV1QgPC0gIldUIgoKIyBzZXQgc29tZSBwbG90dGluZyBwYXJhbWV0ZXJzCldUX3NoYXBlIDwtIGMoIm5vdFdUIj0xNiwgIldUIj00KQpXVF9hbHBoYSA8LSBjKCJub3RXVCI9MC4yLCAiV1QiPTEuMCkKV1Rfc2l6ZSA8LSBjKCJub3RXVCI9MC4zLCAiV1QiPTAuNSkKQ0xPMl9DTE4yX2N1dG9mZiA8LSAwLjI1CmJhcmNvZGVfY3V0b2ZmIDwtIDIKYWRqcF9jdXRvZmYgPC0gMC4wNQoKIyBleHRyYWN0IHN1YnNldHMgZnJvbSBiaWcgZGF0YSBmcmFtZXMgdG8gdGVzdCBpZiBzaWduaWZpY2FudCBkaWZmZXJlbmNlcwpzdWJzZXRfc2lnbmlmX2hpZ2hPMiA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgc3Vic2V0KHdpZGVfZml0bmVzcywgd2lkZV9maXRuZXNzJG5vcm1fQ0xfTzI+MS4wICYgd2lkZV9maXRuZXNzJHBfZml0X2Fkal9XVF9DTF9PMjwwLjA1KSRzZ1JOQV90YXJnZXQpCnN1YnNldF9zaWduaWZfaGlnaExEIDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBzdWJzZXQod2lkZV9maXRuZXNzLCB3aWRlX2ZpdG5lc3Mkbm9ybV9MRCA+IHdpZGVfZml0bmVzcyRub3JtX0NMX08yICYgd2lkZV9maXRuZXNzJG5vcm1fTEQgPiAwLjkpJHNnUk5BX3RhcmdldCkKCiMgcHJlcGFyZSBwbG90dGluZywgY2FsY3VsYXRlIGxtIHRvIGNoZWNrIGRpc3RhbmNlIGZyb20gaXQgKGFzc3VtaW5nIGxpbmVhciByZWdyZXNzaW9uIGdpdmVzIGJldHRlciAxOjEgdGhhbiBsaW5lIHRocm91Z2ggb3JpZ2luKQpsbV9DTE8yX0NMTjIgPC0gbG0obm9ybV9DTF9OMiB+IG5vcm1fQ0xfTzIsIHdpZGVfZml0bmVzcykKc3VtbWFyeShsbV9DTE8yX0NMTjIpCgpjb3JyZWxhdGlvbiA8LSBjb3IudGVzdCh3aWRlX2ZpdG5lc3Mkbm9ybV9DTF9PMiwgd2lkZV9maXRuZXNzJG5vcm1fQ0xfTjIsIG1ldGhvZCA9ICdzcGVhcm1hbicpCmNvcnJlbGF0aW9uCmNvcnJlbGF0aW9uIDwtIGNvci50ZXN0KHdpZGVfZml0bmVzcyRub3JtX0NMX08yLCB3aWRlX2ZpdG5lc3Mkbm9ybV9DTF9OMiwgbWV0aG9kID0gJ3BlYXJzb24nKQpjb3JyZWxhdGlvbgoKd2lkZV9maXRuZXNzX0NMX09MIDwtIHdpZGVfZml0bmVzcwp3aWRlX2ZpdG5lc3NfQ0xfT0wkZGlzdGFuY2VfbG0gPC0gd2lkZV9maXRuZXNzX0NMX09MJG5vcm1fQ0xfTjIgLSAobG1fQ0xPMl9DTE4yJGNvZWZmaWNpZW50c1sxXSArIGxtX0NMTzJfQ0xOMiRjb2VmZmljaWVudHNbMl0gKiB3aWRlX2ZpdG5lc3NfQ0xfT0wkbm9ybV9DTF9PMikKd2lkZV9maXRuZXNzX0NMX09MW29yZGVyKHdpZGVfZml0bmVzc19DTF9PTCRkaXN0YW5jZV9sbSwgZGVjcmVhc2luZyA9IEZBTFNFKSxdWzE6MTAsXQoKIyBjb2xvcmluZyBhY2NvcmRpbmcgdG8gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24Kd2lkZV9maXRuZXNzX0NMX09MJGRpZmYgPC0gIk5PIgp3aWRlX2ZpdG5lc3NfQ0xfT0wkZGlmZlt3aWRlX2ZpdG5lc3NfQ0xfT0wkZGlzdGFuY2VfbG0gPiAwXSA8LSAiQ0xfTjIiCndpZGVfZml0bmVzc19DTF9PTCRkaWZmW3dpZGVfZml0bmVzc19DTF9PTCRkaXN0YW5jZV9sbSA8IDBdIDwtICJDTF9PMiIKI3dpZGVfZml0bmVzc19DTF9PTCRkaWZmW3dpZGVfZml0bmVzc19DTF9PTCRkaXN0YW5jZV9sbSA+IENMTzJfQ0xOMl9jdXRvZmZdIDwtICJDTF9OMiIgIyBpbiBjYXNlIHdlIGNhcmUgZm9yIGN1dC1vZmYgZm9yIG1pbmltYWwgZGlzdGFuY2UgZnJvbSBsaW5lYXIgcmVncmVzc2lvbgojd2lkZV9maXRuZXNzX0NMX09MJGRpZmZbd2lkZV9maXRuZXNzX0NMX09MJGRpc3RhbmNlX2xtIDwgKC1DTE8yX0NMTjJfY3V0b2ZmKV0gPC0gIkNMX08yIiAjIGluIGNhc2Ugd2UgY2FyZSBmb3IgY3V0LW9mZiBmb3IgbWluaW1hbCBkaXN0YW5jZSBmcm9tIGxpbmVhciByZWdyZXNzaW9uCndpZGVfZml0bmVzc19DTF9PTFt3aWRlX2ZpdG5lc3NfQ0xfT0wkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kZGlmZiA8LSAiV1QiCmRvdHBsb3RfY29sb3JzIDwtIGMoY29sX2NvbmRpdGlvbnMsICJOTyI9IiNkM2QzZDNiMiIsICJvcmFuZ2UiLCAiV1QiPSJibGFjayIpCgojIHByZXBhcmUgbGFiZWxzIGZvciBwbG90CndpZGVfZml0bmVzc19DTF9PTCRkZWxhYmVsIDwtIE5BCndpZGVfZml0bmVzc19DTF9PTCRkZWxhYmVsW3dpZGVfZml0bmVzc19DTF9PTCRkaWZmICE9Ik5PIl0gPC0gYXMuY2hhcmFjdGVyKHdpZGVfZml0bmVzc19DTF9PTCRzZ1JOQV90YXJnZXRbd2lkZV9maXRuZXNzX0NMX09MJGRpZmYgIT0gIk5PIl0pCgpwIDwtIGdncGxvdCh3aWRlX2ZpdG5lc3NfQ0xfT0wsIGFlcyh4PW5vcm1fQ0xfTzIsIHk9bm9ybV9DTF9OMiwgY29sb3I9ZGlmZiwgc2hhcGU9V1Rfbm90X1dUKSkgKyBnZW9tX3BvaW50KGFlcyhzaXplPVdUX25vdF9XVCwgYWxwaGE9V1Rfbm90X1dUKSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1XVF9zaGFwZSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9V1Rfc2l6ZSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPVdUX2FscGhhKSArIHRoZW1lX2xpZ2h0KCkgKyBsYWJzKHk9IldlaWdodGVkIG1lYW4gZml0bmVzcyB2YWx1ZSBhdCBjb250aW51b3VzIGxpZ2h0LCA1JSBDTzIsIDk1JSBOMiwgMCUgTzIiLCB4PSJXZWlnaHRlZCBtZWFuIGZpdG5lc3MgdmFsdWUgYXQgY29udGludW91cyBsaWdodCwgNSUgQ08yLCA3NSUgTjIsIDIwJSBPMiIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArIGdlb21fYWJsaW5lKGludGVyY2VwdD1sbV9DTE8yX0NMTjIkY29lZmZpY2llbnRzWzFdLHNsb3BlPWxtX0NMTzJfQ0xOMiRjb2VmZmljaWVudHNbMl0sbGluZXR5cGU9ImRhc2hlZCIsY29sb3I9ImJsYWNrIiwgbGluZXdpZHRoPWxpbmVfd2lkdGhfZm9yX3Bsb3RzKSArIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gZG90cGxvdF9jb2xvcnMpICsgdGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwgc3RyaXAuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbCA9IE5BLCBjb2xvciA9ICJ3aGl0ZSIpLCBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGNvbG91ciA9ICJibGFjayIsIGhqdXN0ID0gMCksIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT04KSwgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCkpICMrIHhsaW0oLTYsICs3KSAreWxpbSgtNiwrNykKZ2dzYXZlKGZpbGVuYW1lID0gIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvd2ZpdG5lc3NfQ0xOMl9DTE8yX3dpdGhvdXRTaWduaWZfd28tbGFiZWxzLnBkZiIsIHBsb3Q9cCwgd2lkdGg9NCwgaGVpZ2h0PTQsIHVuaXRzPSJjbSIpCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3dmaXRuZXNzX0NMTjJfQ0xPMl93aXRob3V0U2lnbmlmX3dvLWxhYmVscy5wbmciLCBwbG90PXAsIHdpZHRoPTQsIGhlaWdodD00LCB1bml0cz0iY20iKQpwCmBgYAoKVXNlIFdpbGNveG9uIHNpZ25lZCByYW5rIGV4YWN0IHRlc3QgdG8gY2hlY2sgaWYgYmFyY29kZXMgZm9yIHRoZSBzYW1lIHZhcmlhbnQgYmVoYXZlIHNpZ25pZmljaWFudGx5IGRpZmZlcmVudCBiZXR3ZWVuIHRoZSB0d28gdGVzdGVkIGNvbmRpdGlvbnMuCgpgYGB7ciB3aWxjb3hvbi1OMi1PMn0Kc3Vic2V0X3NpZ25pZl9oaWdoTzIgPC0gc3Vic2V0KHN1YnNldF9zaWduaWZfaGlnaE8yLCBzdWJzZXRfc2lnbmlmX2hpZ2hPMiRjb25kaXRpb24gJWluJSBjKCJDTF9OMiIsICJDTF9PMiIpKQoKZ2V0X2NvbnRyb2xzIDwtIGZ1bmN0aW9uKGNvbmRfc3BlYywgc2dSTkFfc3BlYyl7CiAgY29udHJvbF90YWJsZSA8LSBzdWJzZXRfc2lnbmlmX2hpZ2hPMltzdWJzZXRfc2lnbmlmX2hpZ2hPMiRjb25kaXRpb24gIT0gdW5pcXVlKGNvbmRfc3BlYykgJiBzdWJzZXRfc2lnbmlmX2hpZ2hPMiRzZ1JOQV90YXJnZXQgPT0gdW5pcXVlKHNnUk5BX3NwZWMpICYgc3Vic2V0X3NpZ25pZl9oaWdoTzIkdGltZSA9PSAwLF0KICBjb250cm9sX3RhYmxlJGZpdG5lc3MKfQoKc3Vic2V0X3NpZ25pZl9oaWdoTzIgPC0gZHBseXI6OmxlZnRfam9pbigKICBzdWJzZXRfc2lnbmlmX2hpZ2hPMiwKICBzdWJzZXRfc2lnbmlmX2hpZ2hPMiAlPiUKICAgIGRwbHlyOjpncm91cF9ieShzZ1JOQV90YXJnZXQsIGNvbmRpdGlvbiwgdGltZSkgJT4lCiAgICBkcGx5cjo6c3VtbWFyaXplKAogICAgICAuZ3JvdXBzID0gImtlZXAiLAogICAgICAjIGFwcGx5IFdpbGNveG9uIHJhbmsgc3VtIHRlc3QgYWdhaW5zdCBvdGhlciBjb25kaXRpb24KICAgICAgcF9maXRuZXNzX2NvbmRpdGlvbiA9IHN0YXRzOjp3aWxjb3gudGVzdCgKICAgICAgICB4ID0gZml0bmVzcywKICAgICAgICB5ID0gZ2V0X2NvbnRyb2xzKGNvbmRpdGlvbiwgc2dSTkFfdGFyZ2V0KSwKICAgICAgICBwYWlyZWQgPSBUUlVFLAogICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIKICAgICAgICApJHAudmFsdWUKICAgICAgKSwKICBieSA9IGMoInNnUk5BX3RhcmdldCIsICJjb25kaXRpb24iLCAidGltZSIpCiAgKSAKCnN1YnNldF9zaWduaWZfaGlnaE8yIDwtIHN1YnNldF9zaWduaWZfaGlnaE8yICU+JQogIGdyb3VwX2J5KGNvbmRpdGlvbiwgdGltZSkgJT4lCiAgbXV0YXRlKAogICAgcF9maXRuZXNzX2NvbmRpdGlvbl9hZGogPSBzdGF0czo6cC5hZGp1c3QocF9maXRuZXNzX2NvbmRpdGlvbiwgbWV0aG9kID0gIkJIIikKICAgICkKYGBgCgpQbG90IHBhcnQgb2YgZGF0YSBzZXQgd2hpY2ggd2FzIHRlc3RlZCBmb3Igc2lnbmlmaWNhbnQgZGlmZmVyZW5jZXMgYW5kIGhpZ2hsaWdodCB2YXJpYW50cyB3aGljaCB3ZXJlIGZvdW5kIHRvIGJlIHNpZ25pZmljYW50LgoKYGBge3IgZml0bmVzcy1maXRuZXNzLXBsb3QtV2lsY294b24tc2lnbmlmaWNhbnR9CnN1YnNldF9zaWduaWZfaGlnaE8yX3JlZCA8LSB1bmlxdWUoc3Vic2V0X3NpZ25pZl9oaWdoTzJbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJudW1fYmFyY29kZXMiLCAicF9maXRuZXNzX2NvbmRpdGlvbl9hZGoiKV0pCnN1YnNldF9zaWduaWZfaGlnaE8yX3JlZCA8LSBwaXZvdF93aWRlcihzdWJzZXRfc2lnbmlmX2hpZ2hPMl9yZWQsIHZhbHVlc19mcm9tID1jKG5vcm0scF9maXRuZXNzX2NvbmRpdGlvbl9hZGopLCBuYW1lc19mcm9tPWNvbmRpdGlvbikKCiMgYWxwaGEsIHNpemUgYW5kIGxhYmVscyBhY2NvcmRpbmcgdG8gc2lnbmlmaWNhbmNlCndpZGVfZml0bmVzc19DTF9PTCRzaWduaWYgPC0gIk5PIgp3aWRlX2ZpdG5lc3NfQ0xfT0wkc2lnbmlmW3dpZGVfZml0bmVzc19DTF9PTCRzZ1JOQV90YXJnZXQgJWluJSB1bmlxdWUoc3Vic2V0KHN1YnNldF9zaWduaWZfaGlnaE8yLCBzdWJzZXRfc2lnbmlmX2hpZ2hPMiRwX2ZpdG5lc3NfY29uZGl0aW9uX2FkaiA8IGFkanBfY3V0b2ZmKSkkc2dSTkFfdGFyZ2V0XSA8LSAiU0lHIgp3aWRlX2ZpdG5lc3NfQ0xfT0wkZGVsYWJlbCA8LSBOQQp3aWRlX2ZpdG5lc3NfQ0xfT0wkZGVsYWJlbFt3aWRlX2ZpdG5lc3NfQ0xfT0wkc2lnbmlmID09IlNJRyJdIDwtIGFzLmNoYXJhY3Rlcih3aWRlX2ZpdG5lc3NfQ0xfT0wkc2dSTkFfdGFyZ2V0W3dpZGVfZml0bmVzc19DTF9PTCRzaWduaWYgPT0iU0lHIl0pCndpZGVfZml0bmVzc19DTF9PTFt3aWRlX2ZpdG5lc3NfQ0xfT0wkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kc2lnbmlmIDwtICJXVCIgIyB0byBlbnN1cmUgV1QgaXMgdmlzaWJsZQpzaWduaWZfc2l6ZSA8LSBjKCJOTyI9MC4zLCAiU0lHIj0wLjUsICJXVCI9MC41KQpzaWduaWZfYWxwaGEgPC0gYygiTk8iPTAuMiwgIlNJRyI9MC45LCAiV1QiPTEuMCkKCnAgPC0gZ2dwbG90KHdpZGVfZml0bmVzc19DTF9PTCwgYWVzKHg9bm9ybV9DTF9PMiwgeT1ub3JtX0NMX04yLCBjb2xvcj1kaWZmLCBsYWJlbD1kZWxhYmVsLCBzaGFwZT1XVF9ub3RfV1QpKSArIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9V1Rfc2hhcGUpICsgZ2VvbV9wb2ludChhZXMoc2l6ZT1zaWduaWYsIGFscGhhPXNpZ25pZiksIHNob3cubGVnZW5kPUZBTFNFKSArIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcz1zaWduaWZfc2l6ZSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPXNpZ25pZl9hbHBoYSkgKyB0aGVtZV9saWdodCgpICsgbGFicyh5PSJXZWlnaHRlZCBtZWFuIGZpdG5lc3MgdmFsdWUgYXQgY29udGludW91cyBsaWdodCwgNSUgQ08yLCA5NSUgTjIsIDAlIE8yIiwgeD0iV2VpZ2h0ZWQgbWVhbiBmaXRuZXNzIHZhbHVlIGF0IGNvbnRpbnVvdXMgbGlnaHQsIDUlIENPMiwgNzUlIE4yLCAyMCUgTzIiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKyBnZW9tX2FibGluZShpbnRlcmNlcHQ9bG1fQ0xPMl9DTE4yJGNvZWZmaWNpZW50c1sxXSxzbG9wZT1sbV9DTE8yX0NMTjIkY29lZmZpY2llbnRzWzJdLGxpbmV0eXBlPSJkYXNoZWQiLGNvbG9yPSJibGFjayIsIGxpbmV3aWR0aD1saW5lX3dpZHRoX2Zvcl9wbG90cykgKyBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGRvdHBsb3RfY29sb3JzKSArIGdlb21fdGV4dF9yZXBlbChmb250ZmFjZT0iaXRhbGljIikgKyB4bGltKDAuNzUsIDIuNSkgK3lsaW0oMC43NSwxLjc1KQpwCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3dmaXRuZXNzX0NMTjJfQ0xPMl9XaWxjb3hfc2lnbmlmLnBkZiIsIHBsb3Q9cCwgd2lkdGg9MTIuNSwgaGVpZ2h0PTEyLjUsIHVuaXRzPSJjbSIpCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3dmaXRuZXNzX0NMTjJfQ0xPMl9XaWxjb3hfc2lnbmlmLnBuZyIsIHBsb3Q9cCwgd2lkdGg9MTIuNSwgaGVpZ2h0PTEyLjUsIHVuaXRzPSJjbSIpCgp3aWRlX2ZpdG5lc3NfQ0xfT0xbd2lkZV9maXRuZXNzX0NMX09MJHNpZ25pZj09IlNJRyIsXVtvcmRlcih3aWRlX2ZpdG5lc3NfQ0xfT0xbd2lkZV9maXRuZXNzX0NMX09MJHNpZ25pZj09IlNJRyIsXSRub3JtX0NMX08yLCBkZWNyZWFzaW5nPVRSVUUpLF0KYGBgCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzIDwtIGMoIkgzNThJIiwgIkszNjBJIiwgIk0zMjhJIiwgIlkzMzNUIiwgIkgzNThRIiwgIlkzMjJNIiwgIkQzMjlUIiwgIlkzMzNWIiwgIks5OUUiLCAiTTIzMUYiLCAiWTMzM0kiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KQpwb3NzaWJseV9zcGVjaWZpY192YXJpYW50cyA8LSBjKCJIMzU4SSIsICJLMzYwSSIsICJNMzI4SSIsICJZMzMzVCIsICJZMzIyTSIsICJEMzI5VCIsICJLOTlFIiwgIk0yMzFGIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkKZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiSDM1OEkiPSIjMzMyMjg4ZmYiLCAiSzM2MEkiPSIjMTE3NzMzZmYiLCAiTTMyOEkiPSIjNDRhYTk5ZmYiLCAiWTMzM1QiPSIjODhjY2VlZmYiLCAiWTMyMk0iPSIjZGRjYzc3ZmYiLCAiRDMyOVQiPSIjY2M2Njc3ZmYiLCAiSzk5RSI9IiNhYTQ0OTlmZiIsICJNMjMxRiI9IiM4ODIyNTVmZiIsICJXVCI9ImJsYWNrIiwgbmVnX2NvbnRyb2xfSzIxND0iZGFya2dyYXkiKQpwb3NzaWJseV9zcGVjaWZpY19zdWJzZXQgPC0gdW5pcXVlKHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBwb3NzaWJseV9zcGVjaWZpY192YXJpYW50cykpCnBvc3NpYmx5X3NwZWNpZmljX3N1YnNldCRzZ1JOQV90YXJnZXQgPC0gZmFjdG9yKHBvc3NpYmx5X3NwZWNpZmljX3N1YnNldCRzZ1JOQV90YXJnZXQsIGxldmVscz1jKHVuaXF1ZShzdWJzZXQocG9zc2libHlfc3BlY2lmaWNfc3Vic2V0LCAhcG9zc2libHlfc3BlY2lmaWNfc3Vic2V0JHNnUk5BX3RhcmdldCAlaW4lIGMobmVnX2NvbnRyb2xfSzIxNCwgIldUIikpJHNnUk5BX3RhcmdldCksIG5lZ19jb250cm9sX0syMTQsICJXVCIpKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocG9zc2libHlfc3BlY2lmaWNfc3Vic2V0KSArIGxhYnModGl0bGU9Im1vc3QgZGlmZmVyZW50IHZhcmlhbnRzIE4yLCBPMiBmZWVkcyIpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpICMrIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXM9YygiV1QiPSJzb2xpZCIsIHBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzWzldPTQ0LCBwb3NzaWJseV9zcGVjaWZpY192YXJpYW50c1sxXT04OCwgcG9zc2libHlfc3BlY2lmaWNfdmFyaWFudHNbMl09MTMsIHBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzWzNdPTEzNDMsIHBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzWzRdPTEzMTM0MywgcG9zc2libHlfc3BlY2lmaWNfdmFyaWFudHNbNV09NzMsIHBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzWzZdPTIyNjIsIHBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzWzddPTExMjIzMywgcG9zc2libHlfc3BlY2lmaWNfdmFyaWFudHNbOF09MTEyMjMzNDQpKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9tb3N0RGlmZmVyZW50X0NMTjJfQ0xPMl90aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wLCB3aWR0aD0xMCwgaGVpZ2h0PTEwKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvbW9zdERpZmZlcmVudF9DTE4yX0NMTzJfdmFyaWFudHNfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCwgd2lkdGg9MTAsIGhlaWdodD0xMCkKYSA8LSBwaXZvdF93aWRlcih1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIHBvc3NpYmx5X3NwZWNpZmljX3ZhcmlhbnRzKVssYygic2dSTkFfdGFyZ2V0IiwgImNvbmRpdGlvbiIsICJub3JtIiwgInBfZml0X2Fkal9XVCIsICJudW1fYmFyY29kZXMiKV0pLCB2YWx1ZXNfZnJvbT1jKCJub3JtIiwgInBfZml0X2Fkal9XVCIpLCBuYW1lc19mcm9tPWNvbmRpdGlvbikKYSRub3JtX211bHRpcGx5IDwtIGEkbm9ybV9DTF9OMiAqIGEkbm9ybV9DTF9PMiAqIGEkbm9ybV9MRAphW29yZGVyKGEkbm9ybV9tdWx0aXBseSwgZGVjcmVhc2luZyA9IFRSVUUpLF0KYGBgCgojIyBDb21wYXJlIGxpZ2h0LWRhcmsgYW5kIGNvbnRpbnVvdXMgbGlnaHQgYXQgdGhlIHNhbWUgZ2FzIGZlZWQKCmBgYHtyIGZpdG5lc3MtZml0bmVzcy1wbG90cy1PMi1MRH0KbG1fQ0xPMl9MRCA8LSBsbShub3JtX0xEIH4gbm9ybV9DTF9PMiwgd2lkZV9maXRuZXNzKQpzdW1tYXJ5KGxtX0NMTzJfTEQpCgpjb3JyZWxhdGlvbiA8LSBjb3IudGVzdCh3aWRlX2ZpdG5lc3Mkbm9ybV9DTF9PMiwgd2lkZV9maXRuZXNzJG5vcm1fTEQsIG1ldGhvZCA9ICdzcGVhcm1hbicpCmNvcnJlbGF0aW9uCmNvcnJlbGF0aW9uIDwtIGNvci50ZXN0KHdpZGVfZml0bmVzcyRub3JtX0NMX08yLCB3aWRlX2ZpdG5lc3Mkbm9ybV9MRCwgbWV0aG9kID0gJ3BlYXJzb24nKQpjb3JyZWxhdGlvbgoKQ0xfTERfY3V0b2ZmIDwtIDAuMzIKYmFyY29kZV9jdXRvZmYgPC0gMgphZGpwX2N1dG9mZiA8LSAwLjA1Cgp3aWRlX2ZpdG5lc3NfTERfT0wgPC0gd2lkZV9maXRuZXNzCgp3aWRlX2ZpdG5lc3NfTERfT0wkZGlzdGFuY2VfbG0gPC0gd2lkZV9maXRuZXNzX0xEX09MJG5vcm1fTEQgLSAobG1fQ0xPMl9MRCRjb2VmZmljaWVudHNbMV0gKyBsbV9DTE8yX0xEJGNvZWZmaWNpZW50c1syXSAqIHdpZGVfZml0bmVzc19MRF9PTCRub3JtX0NMX08yKQp3aWRlX2ZpdG5lc3NfTERfT0xbb3JkZXIod2lkZV9maXRuZXNzX0xEX09MJGRpc3RhbmNlX2xtLCBkZWNyZWFzaW5nID0gVFJVRSksXVsxOjEwLF0KCiMgY29sb3JpbmcgYWNjb3JkaW5nIHRvIGRpZmZlcmVudGlhbCBleHByZXNzaW9uCndpZGVfZml0bmVzc19MRF9PTCRkaWZmIDwtICJOTyIKd2lkZV9maXRuZXNzX0xEX09MJGRpZmZbd2lkZV9maXRuZXNzX0xEX09MJGRpc3RhbmNlX2xtID4gMF0gPC0gIkxEIgp3aWRlX2ZpdG5lc3NfTERfT0wkZGlmZlt3aWRlX2ZpdG5lc3NfTERfT0wkZGlzdGFuY2VfbG0gPCAwXSA8LSAiQ0xfTzIiCiN3aWRlX2ZpdG5lc3NfTERfT0wkZGlmZlt3aWRlX2ZpdG5lc3NfTERfT0wkZGlzdGFuY2VfbG0gPiBDTF9MRF9jdXRvZmZdIDwtICJMRCIgIyBpZiB1c2luZyBjdXQtb2ZmCiN3aWRlX2ZpdG5lc3NfTERfT0wkZGlmZlt3aWRlX2ZpdG5lc3NfTERfT0wkZGlzdGFuY2VfbG0gPCAoLUNMX0xEX2N1dG9mZildIDwtICJDTF9PMiIgIyBpZiB1c2luZyBjdXQtb2ZmCndpZGVfZml0bmVzc19MRF9PTFt3aWRlX2ZpdG5lc3NfTERfT0wkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kZGlmZiA8LSAiV1QiCgpwIDwtIGdncGxvdCh3aWRlX2ZpdG5lc3NfTERfT0wsIGFlcyh4PW5vcm1fQ0xfTzIsIHk9bm9ybV9MRCwgY29sb3I9ZGlmZiwgc2hhcGU9V1Rfbm90X1dUKSkgKyBnZW9tX3BvaW50KGFlcyhzaXplPVdUX25vdF9XVCwgYWxwaGE9V1Rfbm90X1dUKSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1XVF9zaGFwZSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9V1Rfc2l6ZSkgKyBzY2FsZV9hbHBoYV9tYW51YWwodmFsdWVzPVdUX2FscGhhKSArIHRoZW1lX2xpZ2h0KCkgKyBsYWJzKHk9IldlaWdodGVkIG1lYW4gZml0bmVzcyB2YWx1ZSBpbiBsaWdodC1kYXJrIGN5Y2xlcywgNSUgQ08yLCA3NSUgTjIsIDIwJSBPMiIsIHg9IldlaWdodGVkIG1lYW4gZml0bmVzcyB2YWx1ZSBpbiBjb250aW51b3VzIGxpZ2h0LCA1JSBDTzIsIDc1JSBOMiwgMjAlIE8yIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpICsgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0PWxtX0NMTzJfTEQkY29lZmZpY2llbnRzWzFdLHNsb3BlPWxtX0NMTzJfTEQkY29lZmZpY2llbnRzWzJdLGxpbmV0eXBlPSJkYXNoZWQiLGNvbG9yPSJibGFjayIpKyBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGRvdHBsb3RfY29sb3JzKSArIHRoZW1lKHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksIHN0cmlwLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvdXIgPSAiYmxhY2siLCBoanVzdCA9IDApLCBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpKSAjKyB4bGltKC02LCArNykgK3lsaW0oLTYsKzcpICArIGdlb21fYWJsaW5lKGludGVyY2VwdD1sbV9DTE8yX0xEJGNvZWZmaWNpZW50c1sxXS1DTF9MRF9jdXRvZmYsIHNsb3BlPWxtX0NMTzJfTEQkY29lZmZpY2llbnRzWzJdLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3I9ImJsYWNrIikgKyBnZW9tX2FibGluZShpbnRlcmNlcHQ9bG1fQ0xPMl9MRCRjb2VmZmljaWVudHNbMV0rQ0xfTERfY3V0b2ZmLCBzbG9wZT1sbV9DTE8yX0xEJGNvZWZmaWNpZW50c1syXSwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yPSJibGFjayIpIApnZ3NhdmUoZmlsZW5hbWUgPSAiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi93Zml0bmVzc19DTE8yX0xEX3dpdGhvdXRTaWduaWZfd2l0aG91dExhYmVsaW5nLnBkZiIsIHBsb3Q9cCwgd2lkdGg9NCwgaGVpZ2h0PTQsIHVuaXRzPSJjbSIpCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3dmaXRuZXNzX0NMTzJfTERfd2l0aG91dFNpZ25pZl93aXRob3V0TGFiZWxpbmcucG5nIiwgcGxvdD1wLCB3aWR0aD00LCBoZWlnaHQ9NCwgdW5pdHM9ImNtIikKcApgYGAKClJ1biBXaWxjb3hvbiB0ZXN0IHRvIGNoZWNrIGZvciBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBiZXR3ZWVuIGNvbmRpdGlvbnMuCgpgYGB7ciB3aWxjb3hvbi1MRC1PMn0Kc3Vic2V0X3NpZ25pZl9oaWdoTEQgPC0gc3Vic2V0KHN1YnNldF9zaWduaWZfaGlnaExELCBzdWJzZXRfc2lnbmlmX2hpZ2hMRCRjb25kaXRpb24gJWluJSBjKCJMRCIsICJDTF9PMiIpICYgc3Vic2V0X3NpZ25pZl9oaWdoTEQkdGltZT09MC4wKQoKZ2V0X2NvbnRyb2xzIDwtIGZ1bmN0aW9uKGNvbmRfc3BlYywgc2dSTkFfc3BlYyl7CiAgY29udHJvbF90YWJsZSA8LSBzdWJzZXRfc2lnbmlmX2hpZ2hMRFtzdWJzZXRfc2lnbmlmX2hpZ2hMRCRjb25kaXRpb24gIT0gdW5pcXVlKGNvbmRfc3BlYykgJiBzdWJzZXRfc2lnbmlmX2hpZ2hMRCRzZ1JOQV90YXJnZXQgPT0gdW5pcXVlKHNnUk5BX3NwZWMpICYgc3Vic2V0X3NpZ25pZl9oaWdoTEQkdGltZSA9PSAwLF0KICBjb250cm9sX3RhYmxlJGZpdG5lc3MKfQoKc3Vic2V0X3NpZ25pZl9oaWdoTEQgPC0gZHBseXI6OmxlZnRfam9pbigKICBzdWJzZXRfc2lnbmlmX2hpZ2hMRCwKICBzdWJzZXRfc2lnbmlmX2hpZ2hMRCAlPiUKICAgIGRwbHlyOjpncm91cF9ieShzZ1JOQV90YXJnZXQsIGNvbmRpdGlvbiwgdGltZSkgJT4lCiAgICBkcGx5cjo6c3VtbWFyaXplKAogICAgICAuZ3JvdXBzID0gImtlZXAiLAogICAgICAjIGFwcGx5IFdpbGNveG9uIHJhbmsgc3VtIHRlc3QgYWdhaW5zdCBvdGhlciBjb25kaXRpb24KICAgICAgcF9maXRuZXNzX2NvbmRpdGlvbiA9IHN0YXRzOjp3aWxjb3gudGVzdCgKICAgICAgICB4ID0gZml0bmVzcywKICAgICAgICB5ID0gZ2V0X2NvbnRyb2xzKGNvbmRpdGlvbiwgc2dSTkFfdGFyZ2V0KSwKICAgICAgICBwYWlyZWQgPSBUUlVFLAogICAgICAgIGFsdGVybmF0aXZlID0gInR3by5zaWRlZCIKICAgICAgICApJHAudmFsdWUKICAgICAgKSwKICBieSA9IGMoInNnUk5BX3RhcmdldCIsICJjb25kaXRpb24iLCAidGltZSIpCiAgKSAKCnN1YnNldF9zaWduaWZfaGlnaExEIDwtIHN1YnNldF9zaWduaWZfaGlnaExEICU+JQogIGdyb3VwX2J5KGNvbmRpdGlvbiwgdGltZSkgJT4lCiAgbXV0YXRlKAogICAgcF9maXRuZXNzX2NvbmRpdGlvbl9hZGogPSBzdGF0czo6cC5hZGp1c3QocF9maXRuZXNzX2NvbmRpdGlvbiwgbWV0aG9kID0gIkJIIikKICAgICkKYGBgCgpgYGB7ciBmaXRuZXNzLWZpdG5lc3MtcGxvdC1XaWxjb3hvbi1zaWduaWZpY2FudC1MRH0Kc3Vic2V0X3NpZ25pZl9oaWdoTERfcmVkIDwtIHVuaXF1ZShzdWJzZXRfc2lnbmlmX2hpZ2hMRFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIsICJwX2ZpdG5lc3NfY29uZGl0aW9uX2FkaiIpXSkKc3Vic2V0X3NpZ25pZl9oaWdoTERfcmVkIDwtIHBpdm90X3dpZGVyKHN1YnNldF9zaWduaWZfaGlnaExEX3JlZCwgdmFsdWVzX2Zyb20gPWMobm9ybSxwX2ZpdG5lc3NfY29uZGl0aW9uX2FkaiksIG5hbWVzX2Zyb209Y29uZGl0aW9uKQoKIyBsYWJlbHMsIGFscGhhIGFuZCBzaXplIGFjY29yZGluZyB0byBzaWduaWZpY2FuY2UKd2lkZV9maXRuZXNzX0xEX09MJHNpZ25pZiA8LSAiTk8iCndpZGVfZml0bmVzc19MRF9PTCRzaWduaWZbd2lkZV9maXRuZXNzX0xEX09MJHNnUk5BX3RhcmdldCAlaW4lIHVuaXF1ZShzdWJzZXQoc3Vic2V0X3NpZ25pZl9oaWdoTEQsIHN1YnNldF9zaWduaWZfaGlnaExEJHBfZml0bmVzc19jb25kaXRpb25fYWRqIDwgMC40KSkkc2dSTkFfdGFyZ2V0XSA8LSAiU0lHIgp3aWRlX2ZpdG5lc3NfTERfT0wkZGVsYWJlbCA8LSBOQQp3aWRlX2ZpdG5lc3NfTERfT0wkZGVsYWJlbFt3aWRlX2ZpdG5lc3NfTERfT0wkc2lnbmlmID09IlNJRyJdIDwtIGFzLmNoYXJhY3Rlcih3aWRlX2ZpdG5lc3NfTERfT0wkc2dSTkFfdGFyZ2V0W3dpZGVfZml0bmVzc19MRF9PTCRzaWduaWYgPT0iU0lHIl0pCndpZGVfZml0bmVzc19DTF9PTFt3aWRlX2ZpdG5lc3NfQ0xfT0wkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kc2lnbmlmIDwtICJXVCIgIyB0byBlbnN1cmUgV1QgaXMgdmlzaWJsZQoKcCA8LSBnZ3Bsb3Qod2lkZV9maXRuZXNzX0xEX09MLCBhZXMoeD1ub3JtX0NMX08yLCB5PW5vcm1fTEQsIGNvbG9yPWRpZmYsIGxhYmVsPWRlbGFiZWwsIHNoYXBlPVdUX25vdF9XVCkpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1XVF9zaGFwZSkgKyBnZW9tX3BvaW50KGFlcyhzaXplPXNpZ25pZiwgYWxwaGE9c2lnbmlmKSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzPXNpZ25pZl9zaXplKSArIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXM9c2lnbmlmX2FscGhhKSArIHRoZW1lX2xpZ2h0KCkgKyBsYWJzKHk9IldlaWdodGVkIG1lYW4gZml0bmVzcyB2YWx1ZSBhdCBjb250aW51b3VzIGxpZ2h0LCA1JSBDTzIsIDk1JSBOMiwgMCUgTzIiLCB4PSJXZWlnaHRlZCBtZWFuIGZpdG5lc3MgdmFsdWUgYXQgY29udGludW91cyBsaWdodCwgNSUgQ08yLCA3NSUgTjIsIDIwJSBPMiIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArIGdlb21fYWJsaW5lKGludGVyY2VwdD1sbV9DTE8yX0xEJGNvZWZmaWNpZW50c1sxXSxzbG9wZT1sbV9DTE8yX0xEJGNvZWZmaWNpZW50c1syXSxsaW5ldHlwZT0iZGFzaGVkIixjb2xvcj0iYmxhY2siKSArIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gZG90cGxvdF9jb2xvcnMpICsgZ2VvbV90ZXh0X3JlcGVsKCkgIysgeGxpbSgwLjc1LCAyLjUpICt5bGltKDAuNzUsMi4yNSkKcApnZ3NhdmUoZmlsZW5hbWUgPSAiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi93Zml0bmVzc19MRF9DTE8yX1dpbGNveF9zaWduaWYucGRmIiwgcGxvdD1wLCB3aWR0aD0xMi41LCBoZWlnaHQ9MTIuNSwgdW5pdHM9ImNtIikKZ2dzYXZlKGZpbGVuYW1lID0gIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvd2ZpdG5lc3NfTERfQ0xPMl9XaWxjb3hfc2lnbmlmLnBuZyIsIHBsb3Q9cCwgd2lkdGg9MTIuNSwgaGVpZ2h0PTEyLjUsIHVuaXRzPSJjbSIpCgp3aWRlX2ZpdG5lc3NfQ0xfT0xbd2lkZV9maXRuZXNzX0xEX09MJHNpZ25pZj09IlNJRyIsXVtvcmRlcih3aWRlX2ZpdG5lc3NfTERfT0xbd2lkZV9maXRuZXNzX0xEX09MJHNpZ25pZj09IlNJRyIsXSRub3JtX0xELCBkZWNyZWFzaW5nPVRSVUUpLF0KYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KIyMgY29sb3JpbmcgYWNjb3JkaW5nIHRvIGRpZmZlcmVudGlhbCBleHByZXNzaW9uCndpZGVfZml0bmVzc19MRF9PTCRkaWZmIDwtICJOTyIKd2lkZV9maXRuZXNzX0xEX09MJGRpZmZbd2lkZV9maXRuZXNzX0xEX09MJGRpc3RhbmNlX2xtID4gQ0xfTERfY3V0b2ZmXSA8LSAiTEQiICMgaWYgdXNpbmcgY3V0LW9mZgp3aWRlX2ZpdG5lc3NfTERfT0wkZGlmZlt3aWRlX2ZpdG5lc3NfTERfT0wkZGlzdGFuY2VfbG0gPCAoLUNMX0xEX2N1dG9mZildIDwtICJDTF9PMiIgIyBpZiB1c2luZyBjdXQtb2ZmCndpZGVfZml0bmVzc19MRF9PTFt3aWRlX2ZpdG5lc3NfTERfT0wkc2dSTkFfdGFyZ2V0PT0iV1QiLF0kZGlmZiA8LSAiV1QiCgojIGxhYmVscywgYWxwaGEgYW5kIHNpemUgYWNjb3JkaW5nIHRvIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgdG8gV1QgdmFyaWFudAp3aWRlX2ZpdG5lc3NfTERfT0wkc2lnbmlmIDwtICJOTyIKd2lkZV9maXRuZXNzX0xEX09MJHNpZ25pZlt3aWRlX2ZpdG5lc3NfTERfT0wkcF9maXRfYWRqX1dUX0NMX08yIDwgYWRqcF9jdXRvZmYgJiB3aWRlX2ZpdG5lc3NfTERfT0wkcF9maXRfYWRqX1dUX0xEIDwgYWRqcF9jdXRvZmYgJiAod2lkZV9maXRuZXNzX0xEX09MJGRpZmY9PSJMRCIgfCB3aWRlX2ZpdG5lc3NfTERfT0wkZGlmZj09IkNMX08yIikgJiB3aWRlX2ZpdG5lc3NfTERfT0wkbnVtX2JhcmNvZGVzID49IGJhcmNvZGVfY3V0b2ZmXSA8LSAiU0lHIgpzaWduaWZfc2l6ZSA8LSBjKCJOTyI9MC4yLCAiU0lHIj0wLjMpCnNpZ25pZl9hbHBoYSA8LSBjKCJOTyI9MC4yLCAiU0lHIj0wLjgpCndpZGVfZml0bmVzc19MRF9PTCRkZWxhYmVsIDwtIGFzLmNoYXJhY3Rlcih3aWRlX2ZpdG5lc3NfTERfT0wkc2dSTkFfdGFyZ2V0KQp3aWRlX2ZpdG5lc3NfTERfT0wkZGVsYWJlbFt3aWRlX2ZpdG5lc3NfTERfT0wkc2lnbmlmICE9IlNJRyJdIDwtIE5BCgpwIDwtIGdncGxvdCh3aWRlX2ZpdG5lc3NfTERfT0wsIGFlcyh4PW5vcm1fQ0xfTzIsIHk9bm9ybV9MRCwgY29sb3I9ZGlmZiwgc2hhcGU9V1Rfbm90X1dULCBsYWJlbD1kZWxhYmVsKSkgKyBnZW9tX3BvaW50KGFlcyhzaXplPXNpZ25pZiwgYWxwaGE9c2lnbmlmKSwgc2hvdy5sZWdlbmQ9RkFMU0UpICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1XVF9zaGFwZSkgKyBzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXM9c2lnbmlmX3NpemUpICsgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcz1zaWduaWZfYWxwaGEpICsgdGhlbWVfbGlnaHQoKSArIGxhYnMoeT0iV2VpZ2h0ZWQgbWVhbiBmaXRuZXNzIHZhbHVlIGluIGxpZ2h0LWRhcmsgY3ljbGVzLCA1JSBDTzIsIDc1JSBOMiwgMjAlIE8yIiwgeD0iV2VpZ2h0ZWQgbWVhbiBmaXRuZXNzIHZhbHVlIGluIGNvbnRpbnVvdXMgbGlnaHQsIDUlIENPMiwgNzUlIE4yLCAyMCUgTzIiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKyBnZW9tX2FibGluZShpbnRlcmNlcHQ9bG1fQ0xPMl9MRCRjb2VmZmljaWVudHNbMV0sc2xvcGU9bG1fQ0xPMl9MRCRjb2VmZmljaWVudHNbMl0sbGluZXR5cGU9ImRhc2hlZCIsY29sb3I9ImJsYWNrIikrIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gZG90cGxvdF9jb2xvcnMpICArIGdlb21fYWJsaW5lKGludGVyY2VwdD1sbV9DTE8yX0xEJGNvZWZmaWNpZW50c1sxXS1DTF9MRF9jdXRvZmYsIHNsb3BlPWxtX0NMTzJfTEQkY29lZmZpY2llbnRzWzJdLCBsaW5ldHlwZT0iZGFzaGVkIiwgY29sb3I9ImJsYWNrIikgKyBnZW9tX2FibGluZShpbnRlcmNlcHQ9bG1fQ0xPMl9MRCRjb2VmZmljaWVudHNbMV0rQ0xfTERfY3V0b2ZmLCBzbG9wZT1sbV9DTE8yX0xEJGNvZWZmaWNpZW50c1syXSwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yPSJibGFjayIpICsgZ2VvbV90ZXh0X3JlcGVsKCkgIyAgKyB4bGltKC02LCArNykgK3lsaW0oLTYsKzcpCmdnc2F2ZShmaWxlbmFtZSA9ICIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL3dmaXRuZXNzX0NMTzJfTERfYXR0ZW1wdF9maW5kRGlmZmVyZW50VmFyaWFudHMucGRmIiwgcGxvdD1wLCB3aWR0aD0xMi41LCBoZWlnaHQ9MTIuNSwgdW5pdHM9ImNtIikKZ2dzYXZlKGZpbGVuYW1lID0gIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvd2ZpdG5lc3NfQ0xPMl9MRF9hdHRlbXB0X2ZpbmREaWZmZXJlbnRWYXJpYW50cy5wbmciLCBwbG90PXAsIHdpZHRoPTEyLjUsIGhlaWdodD0xMi41LCB1bml0cz0iY20iKQpwCgp3aWRlX2ZpdG5lc3NfTERfT0xfc2lnIDwtIHN1YnNldCh3aWRlX2ZpdG5lc3NfTERfT0wsIHdpZGVfZml0bmVzc19MRF9PTCRzaWduaWY9PSJTSUciKQp3aWRlX2ZpdG5lc3NfTERfT0xfc2lnW29yZGVyKHdpZGVfZml0bmVzc19MRF9PTF9zaWckZGlzdGFuY2VfbG0sIGRlY3JlYXNpbmcgPSBUUlVFKSxdWzE6MTAsXQpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpwb3NzaWJseV9oaWdoZXJfaW5MRCA8LSBjKCJLNDU2SCIsICJHNDMyUyIsICJLOTlOIiwgIkgxMjZHIiwiV1QiLCBuZWdfY29udHJvbF9LMjE0KQpkaWZmZXJlbnRfY29sb3JzX3NldCA8LSBjKCJLNDU2SCI9IiMzMzIyODhmZiIsICJHNDMyUyI9IiMxMTc3MzNmZiIsICJIMzU4TSI9IiM0NGFhOTlmZiIsICJIMTI2TiI9IiM4OGNjZWVmZiIsICJIMTI2RyI9IiNkZGNjNzdmZiIsICJLOTlOIj0iI2NjNjY3N2ZmIiwgIldUIj0iYmxhY2siLCBuZWdfY29udHJvbF9LMjE0PSJkYXJrZ3JheSIpCnNpbmdsZV9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIHBvc3NpYmx5X2hpZ2hlcl9pbkxEKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIoc2luZ2xlX3N1YnNldCkgKyBsYWJzKHRpdGxlPSJtb3N0IGRpZmZlcmVudCB2YXJpYW50cyIpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL21vc3REaWZmZXJlbnRfTERfQ0xPMl90aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wLCB3aWR0aD0xMCwgaGVpZ2h0PTEwKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvbW9zdERpZmZlcmVudF9MRF9DTE8yX3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXAsIHdpZHRoPTEwLCBoZWlnaHQ9MTApCgp1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIHBvc3NpYmx5X2hpZ2hlcl9pbkxEKVssYygic2dSTkFfdGFyZ2V0IiwgImNvbmRpdGlvbiIsICJub3JtIiwgInBfZml0X2Fkal9XVCIsICJudW1fYmFyY29kZXMiKV0pCmBgYAoKIyBHZW5lcmFsbHkgaW50ZXJlc3RpbmcgbXV0YW50cwoKSW4gdGhlIGZvbGxvd2luZywgYW4gYXR0ZW1wdCBpcyBtYWRlIHRvIHBpbnBvaW50IHNvbWUgaW50ZXJlc3RpbmcgdmFyaWFudHMgdGhhdCBhcmUgd29ydGggdGVzdGluZy4gVGhlIGZvbGxvd2luZyBjaHVuayBvZiBjb2RlIHNlbGVjdHMgYWxsIHZhcmlhbnRzIHdoaWNoIGFyZSBwZXJmb3JtaW5nIHdlbGwgdW5kZXIgYWxsIGNvbmRpdGlvbnMgYW5kIGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudCB0byB0aGUgYmFzZSB2YXJpYW50LiBGdXJ0aGVybW9yZSwgb25seSB2YXJpYW50cyB3aXRoIGF0IGxlYXN0IHR3byBiYXJjb2RlcyBhcmUgdGFrZW4gaW50byBhY2NvdW50LgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CmNvbHVtbnMgPC0gYygic2dSTkFfdGFyZ2V0IiwgICJjb25kaXRpb24iLCAibm9ybSIsICJwX2ZpdF9hZGpfV1QiLCAibnVtX2JhcmNvZGVzIiwgIm51bWJlcl9tdXRzIikKZml0bmVzc19kYXRhX2dvb2RWYXJpYW50cyA8LSBwaXZvdF93aWRlcih1bmlxdWUoZml0bmVzc19kYXRhWyxjb2x1bW5zXSksIHZhbHVlc19mcm9tID0gYygibm9ybSIsICJwX2ZpdF9hZGpfV1QiKSwgbmFtZXNfZnJvbSA9IGNvbmRpdGlvbikKZml0bmVzc19kYXRhX2dvb2RWYXJpYW50cyA8LSB1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMsIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkcF9maXRfYWRqX1dUX0NMX04yIDwgMC4wNSAmIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkcF9maXRfYWRqX1dUX0NMX08yIDwgMC4wNSAmIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkcF9maXRfYWRqX1dUX0xEIDwgMC4wNSAmIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkbm9ybV9DTF9OMiA+IDEgJiBmaXRuZXNzX2RhdGFfZ29vZFZhcmlhbnRzJG5vcm1fQ0xfTzIgPiAxICYgZml0bmVzc19kYXRhX2dvb2RWYXJpYW50cyRub3JtX0xEID4gMSAmIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkbnVtX2JhcmNvZGVzID4gMSkpCgpsZW5ndGgodW5pcXVlKGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkc2dSTkFfdGFyZ2V0KSkKbGVuZ3RoKHVuaXF1ZShmaXRuZXNzX2RhdGFfZ29vZFZhcmlhbnRzJHNnUk5BX3RhcmdldCkpL2xlbmd0aCh1bmlxdWUoZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCkpCgpmaXRuZXNzX2RhdGFfZ29vZFZhcmlhbnRzJGNvbWJpbmVkX25vcm1zIDwtIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkbm9ybV9DTF9OMiAqIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkbm9ybV9DTF9PMiAqIGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkbm9ybV9MRApwcmludChmaXRuZXNzX2RhdGFfZ29vZFZhcmlhbnRzW29yZGVyKGZpdG5lc3NfZGF0YV9nb29kVmFyaWFudHMkY29tYmluZWRfbm9ybXMsIGRlY3JlYXNpbmcgPSBUUlVFKSxdKQp3cml0ZV9jc3YoZml0bmVzc19kYXRhX2dvb2RWYXJpYW50c1tvcmRlcihmaXRuZXNzX2RhdGFfZ29vZFZhcmlhbnRzJGNvbWJpbmVkX25vcm1zLCBkZWNyZWFzaW5nID0gVFJVRSksXSwgIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9nb29kVmFyaWFudHMuY3N2IikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KaGlnaFNjb3Jpbmdfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJIMTI2TiIsICJIMTI2TSIsICJXMTI4SSIsICJLMzYwSSIsICJLMzYwTSIsICJRMTQyRCIsICJLMjMzQyIsICJIMzU4QyIsICJLMzYwTCIsICJIMzU4TCIsICJLOTlOIiwgIkszNjBDIiwgIlkzMzNWIiwgIkgxMjZHIiwgIk0xNTRFLEsyNjFBLFE0MDZELFM0NDZUIiwgIlExNDJFIiwgIk0zNDVRIiwgIkYxMDRWIiwgIlY5OFEiLCAiSzI2MUEsSDI2NUEsUTQwNkQsUzQ0NkkiLCAiSzk5VCIsICJLMjMzTCIsICJNMzQ1ViIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIoaGlnaFNjb3Jpbmdfc3Vic2V0KSArIGxhYnModGl0bGU9IkZpdHRlc3QgdmFyaWFudHMgd2l0aCA5NSUgQ0kiKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9oaWdoZXN0X3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wZGYiLCBwbG90PXApCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9oaWdoZXN0X3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXApCmBgYAoKUGFydGlhbGx5LCBleGNoYW5nZXMgYXQgdGhlIHNhbWUgYW1pbm8gYWNpZCBwb3NpdGlvbiBzY29yZSBxdWl0ZSBzaW1pbGFybHkuIFdlIGNhbm5vdCBkaXN0aW5ndWlzaCB0aGVpciBmaXRuZXNzIG9uIGJhc2lzIG9mIG91ciBkYXRhLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJIMTI2TiIsICJIMTI2TSIsICJIMTI2RyIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iVmVyeSBmaXQgSDEyNiB2YXJpYW50cyB3aXRoIDk1JSBDSSIpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL0gxMjZfc3Vic2V0X3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wZGYiLCBwbG90PXApCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9IMTI2X3N1YnNldF92YXJpYW50c190aW1lTGluZVBsb3QucG5nIiwgcGxvdD1wKQoKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkszNjBJIiwgIkszNjBNIiwgIkszNjBMIiwgIkszNjBDIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihwbG90X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJWZXJ5IGZpdCBLMzYwIHZhcmlhbnRzIHdpdGggOTUlIENJIikKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvSzM2MF9zdWJzZXRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL0szNjBfc3Vic2V0X3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXApCgpwbG90X3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiSDM1OEMiLCAiSDM1OEwiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IlZlcnkgZml0IEgzNTggdmFyaWFudHMgd2l0aCA5NSUgQ0kiKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9IMzU4X3N1YnNldF92YXJpYW50c190aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvSDM1OF9zdWJzZXRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCkKCnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJRMTQyRCIsICJRMTQyRSIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iVmVyeSBmaXQgUTE0MiB2YXJpYW50cyB3aXRoIDk1JSBDSSIpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL1ExNDJfc3Vic2V0X3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wZGYiLCBwbG90PXApCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BuZy9RMTQyX3N1YnNldF92YXJpYW50c190aW1lTGluZVBsb3QucG5nIiwgcGxvdD1wKQoKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIk0zNDVRIiwgIk0zNDVWIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihwbG90X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJWZXJ5IGZpdCBNMzQ1IHZhcmlhbnRzIHdpdGggOTUlIENJIikKcApnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvTTM0NV9zdWJzZXRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL00zNDVfc3Vic2V0X3ZhcmlhbnRzX3RpbWVMaW5lUGxvdC5wbmciLCBwbG90PXApCgpwbG90X3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiSzIzM0MiLCAiSzIzM0wiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IlZlcnkgZml0IEsyMzMgdmFyaWFudHMgd2l0aCA5NSUgQ0kiKQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9LMjMzX3N1YnNldF92YXJpYW50c190aW1lTGluZVBsb3QucGRmIiwgcGxvdD1wKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wbmcvSzIzM19zdWJzZXRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCkKCnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJLOTlOIiwgIks5OVQiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IlZlcnkgZml0IEs5OSB2YXJpYW50cyB3aXRoIDk1JSBDSSIpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL0s5OV9zdWJzZXRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL0s5OV9zdWJzZXRfdmFyaWFudHNfdGltZUxpbmVQbG90LnBuZyIsIHBsb3Q9cCkKCnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJXMTI4SSIsICJZMzMzViIsICJGMTA0ViIsICJWOThRIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCmRpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIlcxMjhJIj0iIzMzMjI4OGZmIiwgIlkzMzNWIj0iIzExNzczM2ZmIiwgIlY5OFEiPSIjODhjY2VlZmYiLCAiRjEwNFYiPSIjZGRjYzc3ZmYiLCAiV1QiPSJibGFjayIsIG5lZ19jb250cm9sX0syMTQ9ImRhcmtncmF5IikgI2RpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIlcxMjhJIj0iIzMzMjI4OGZmIiwgIlkzMzNWIj0iIzExNzczM2ZmIiwgIkYxMDRWIj0iIzQ0YWE5OWZmIiwgIlY5OFEiPSIjODhjY2VlZmYiLCAiRjEwNFYiPSIjZGRjYzc3ZmYiLCAiSzk5TiI9IiNjYzY2NzdmZiIsICJXVCI9ImJsYWNrIiwgbmVnX2NvbnRyb2xfSzIxND0iZGFya2dyYXkiKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iVmVyeSBmaXQgdmFyaWFudHMgd2l0aCA5NSUgQ0kiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9yZW1haW5pbmdfaGlnaFNjb3JpbmdfdmFyaWFudHNfdGltZUxpbmVQbG90LnBkZiIsIHBsb3Q9cCkKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcG5nL3JlbWFpbmluZ19oaWdoU2NvcmluZ192YXJpYW50c190aW1lTGluZVBsb3QucG5nIiwgcGxvdD1wKQpgYGAKCiMgUGxvdCBlcGlzdGF0aWMgZWZmZWN0cwoKIyMgR2VuZXJhbCBvdmVydmlldyBvdmVyIGVwaXN0YXNpcyBpbiBkYXRhIHNldAoKRnJvbSBNaXRvbiBldCBhbC4sIDIwMTYgKGh0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pL2Z1bGwvMTAuMTAwMi9wcm8uMjg3Nik6IAoiV2UgY2FsY3VsYXRlZCB0aGUgZm9sZCBjaGFuZ2UgaW4gZW56eW1lIGZpdG5lc3MgKEYpIHByb3ZpZGVkIGJ5IGEgbXV0YXRpb24gaSBvbiB0aGUgd2lsZC10eXBlIGJhY2tncm91bmQgKM6URnd0LGk9Rnd0K2kvRnd0KSBhbmQgdGhlIGNoYW5nZSBjYXVzZWQgYnkgdGhlIHNhbWUgbXV0YXRpb24gb24gdGhlIGludGVybWVkaWF0ZSB2YXJpYW50IGogaW4gdGhlIHRyYWplY3RvcnksICjOlEZqLGk9RmoraS9GaikgWy4uLl0gZXBpc3Rhc2lzIHdhcyBkZXRlcm1pbmVkIGJ5IGNvbXBhcmluZyB0aGUgZm9sZCBjaGFuZ2VzIGluIHRoZSB0cmFqZWN0b3J5IG92ZXIgdGhlIHdpbGQtdHlwZSBiYWNrZ3JvdW5kICjOlEZqLGkvzpRGd3QsaSkuIEZvciB0aGUgc2FrZSBvZiBzaW1wbGljaXR5LCB3ZSBjb25zaWRlcmVkIHRoYXQg4omlMS41LWZvbGQgY2hhbmdlIGlzIHNpZ25pZmljYW50LCBhbmQgbGVzcyB0aGFuIDEuNS1mb2xkIGNoYW5nZSBpcyBuZXV0cmFsIChvciBuZWFybHkgbmV1dHJhbCkuIFsuLi5dIE5ldXRyYWwgZGVzaWduYXRlcyBtdXRhdGlvbnMgdGhhdCBzaG93IGxlc3MgdGhhbiAxLjUtZm9sZCBjaGFuZ2UgaW4gZW56eW1lIGZpdG5lc3Mgb24gYm90aCBiYWNrZ3JvdW5kcyAoMC43IDwgW86URmosaSBhbmQgzpRGd3QsaV0gPCAxLjUpLiAoaWkgLSB2KSBGdW5jdGlvbmFsIHJlZmVycyB0byBub24tbmV1dHJhbCBtdXRhdGlvbnMgdGhhdCBleGhpYml0ID4xLjUtZm9sZCBpbXByb3ZlbWVudCBpbiBlaXRoZXIgZ2VuZXRpYyBiYWNrZ3JvdW5kICjOlEZqLGkgb3IgzpRGd3QsaSA+IDEuNSkuIEFtb25nIGZ1bmN0aW9uYWwgbXV0YXRpb25zLCBpaSkgbm8gZXBpc3Rhc2lzIHJlZmVycyB0byBtdXRhdGlvbnMgdGhhdCBkbyBub3Qgc2lnbmlmaWNhbnRseSBhbHRlciB0aGUgZW56eW1lIGZpdG5lc3MgZGVwZW5kaW5nIG9uIHRoZSBiYWNrZ3JvdW5kIChtdXRhdGlvbnMgYXJlIGFkZGl0aXZlLCDOlEZqLGkvzpRGd3QsaSDiiLwgMSkuIChpaWkg4oCTIGl2KSBQb3NpdGl2ZSBlcGlzdGFzaXMgYXBwbGllcyB0byBhIG11dGF0aW9uIHRoYXQgYmVjb21lcyBtb3JlIGJlbmVmaWNpYWwgd2hlbiBjb21iaW5lZCB3aXRoIHByaW9yIHN1YnN0aXR1dGlvbnMgb24gdGhlIHRyYWplY3RvcnksIGNvbXBhcmVkIHRvIGl0cyBlZmZlY3Qgb24gdGhlIHdpbGQtdHlwZSBiYWNrZ3JvdW5kICjOlEZqLGkvzpRGd3QsaSA+IDEuNSkuIEl0IGNhbiBiZSBkaXZpZGVkIGluIHR3byBzdWJjbGFzc2VzOiBpaWkpIFBvc2l0aXZlIG1hZ25pdHVkZSBlcGlzdGFzaXMgcmVmZXJzIHRvIGNhc2VzIHdoZXJlIHRoZSBlZmZlY3QgaXMgbmV1dHJhbCBvciBwb3NpdGl2ZSBvbiB0aGUgd2lsZC10eXBlIGJhY2tncm91bmQgYW5kIGlzIGZ1cnRoZXIgYW1wbGlmaWVkIG9uIHRoZSB0cmFqZWN0b3J5ICjOlEZqLGkg4omrIM6URnd0LOKAiWnigIk+4oCJMC43IG9yIM6URmos4oCJaSDiiasgzpRGd3Qs4oCJaeKAiT7igIkxLjUpOyBhbmQgaXYpIFBvc2l0aXZlIHNpZ24gZXBpc3Rhc2lzLCB3aGljaCByZWZlcnMgdG8gbXV0YXRpb25zIGNhdXNpbmcgYSBkZWxldGVyaW91cyBlZmZlY3Qgb24gdGhlIHdpbGQtdHlwZSBiYWNrZ3JvdW5kIGJ1dCBhIGJlbmVmaWNpYWwgZWZmZWN0IG9uIHRoZSB0cmFqZWN0b3J5ICjOlEZ3dCzigIlpIDwwLjcgYW5kIM6URmos4oCJaeKAiT7igIkxLjUpLiB2KSBOZWdhdGl2ZSBlcGlzdGFzaXMgYXBwbGllcyB0byBhIG11dGF0aW9uIHRoYXQgYmVjb21lcyBsZXNzIGJlbmVmaWNpYWwgb24gdGhlIHRyYWplY3RvcnkgYmFja2dyb3VuZCBjb21wYXJlZCB0byBpdHMgb3JpZ2luYWwgZWZmZWN0IG9uIHRoZSB3aWxkIHR5cGUgKM6URmos4oCJaS/OlEZ3dCzigIlpIDwwLjcpLiIKCmBgYHtyIGxvYWQtZXBpc3Rhc2lzLWRhdGFzZXRzLCBtZXNzYWdlPUZBTFNFLCBlY2hvPUZBTFNFfQplcGlzdGF0aWNfdGFibGUgPC0gcmVhZF90c3YoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9lcGlzdGFzaXMvdGFibGVfdmFsdWVzLnRzdiIpCmVwaXN0YXRpY190YWJsZSRyYXRpb19XVF9oaWdoZXJPcmRlciA8LSBlcGlzdGF0aWNfdGFibGUkVmFyaWFudF9yYXRpby9lcGlzdGF0aWNfdGFibGUkRXhjaGFuZ2VfdmFsdWVfYW5kX3JhdGlvX2luX1dUCmBgYAoKSWRlbnRpZnkgY3V0LW9mZiB2YWx1ZXMgZm9yIGdvb2QgLyBiYWQgdmFyaWFudHMgLSB1c2UgYWRqLiBwIHZhbHVlcyA8IDAuMDUgdG8gZG8gc28sIHF1ZXN0aW9uICJjYW4gSSBzaWduaWZpY2FudGx5IGRpc3Rpbmd1aXNoIHRoaXMgdmFyaWFudCBmcm9tIHRoZSBiYXNlIHZhcmlhbnQ/IgpTZWVtcyBhcyBpZiBkaWZmZXJlbmNlcyBhcyBzbWFsbCBhcyBhIG5vcm1lZCB2YWx1ZSBvZiAxLjEgYXJlIG9mdGVuIGFscmVhZHkgc2lnbmlmaWNhbnQgY29tcGFyZWQgdG8gdGhlIGJhc2UgdmFyaWFudCAtIEkgYXNzdW1lIGEgbm9ybWVkIHZhbHVlIG9mIDEuMjUgaXMgYWJvdXQgcmlnaHQuCgpgYGB7cn0KY29tYmluYXRvcmlhbF93aWRlIDwtIHN1YnNldChjb21iaW5hdG9yaWFsX3dpZGUsIGNvbWJpbmF0b3JpYWxfd2lkZSRwX2ZpdF9hZGpfV1Q8MC4wNSkgIyBpbiBjYXNlIHRoaW5ncyB3ZXJlIGNoYW5nZWQgYWJvdmUKYSA8LSB1bmlxdWUoc3Vic2V0KGNvbWJpbmF0b3JpYWxfd2lkZSwgY29tYmluYXRvcmlhbF93aWRlJG5vcm0gPiAxLjApJG5vcm0pCmEgPC0gYVtvcmRlcihhKV0KYVsxOjIwXQpgYGAKCkFsc28sIGRpZmZlcmVuY2VzIGFzIHNtYWxsIGFzIDAuOSBjb21wYXJlZCB0byAxLjAgY2FuIGJlIGRpc3Rpbmd1aXNoZWQgLSBzbyBJIGd1ZXNzIGEgZ29vZCBjdXQtb2ZmIG1pZ2h0IGJlIDAuOC4gCgpgYGB7cn0KYSA8LSB1bmlxdWUoc3Vic2V0KGNvbWJpbmF0b3JpYWxfd2lkZSwgY29tYmluYXRvcmlhbF93aWRlJG5vcm0gPCAxLjApJG5vcm0pCmEgPC0gYVtvcmRlcihhLCBkZWNyZWFzaW5nPVRSVUUpXQphWzE6MjBdCmBgYAoKYGBge3J9CnN1bW1hcml6ZV9kYXRhZnJhbWUgPC0gdW5pcXVlKGRwbHlyOjpzdW1tYXJpemUoLmRhdGE9ZXBpc3RhdGljX3RhYmxlLAogICAgICAuYnkgPSBjKEV4Y2hhbmdlLCBDb25kaXRpb24pLAogICAgIyB2YWx1ZSBpbiBXVCBiYWNrZ3JvdW5kCiAgICB2YWx1ZV9iYWNrZ3JvdW5kID0gdW5pcXVlKC5kYXRhW1siRXhjaGFuZ2VfdmFsdWVfYW5kX3JhdGlvX2luX1dUIl1dKSwKICAgICMgbnVtYmVyIG5lZy4gZWZmZWN0IGluIGhpZ2hlciBvcmRlcgogICAgbnVtYmVyX25lZ2F0aXZlX2V4Y2hhbmdlcyA9IHN1bSguZGF0YVtbIlZhcmlhbnRfcmF0aW8iXV0gPCAwLjgpLCAjIGFkanVzdGVkIHRvIDAuOCBhY2NvcmRpbmcgdG8gb2JzZXJ2YXRpb25zIGRldGFpbGVkIGFib3ZlIGZyb20gb3JpZ2luYWxseSAwLjcgaW4gcGFwZXIKICAgICMgbnVtYmVyIG5ldXRyYWwgZWZmZWN0IGluIGhpZ2hlciBvcmRlciAKICAgIG51bWJlcl9uZXV0cmFsX2V4Y2hhbmdlcyA9IHN1bSguZGF0YVtbIlZhcmlhbnRfcmF0aW8iXV0gPiAwLjggJiAuZGF0YVtbIlZhcmlhbnRfcmF0aW8iXV0gPCAxLjI1KSwgIyBhZGp1c3RlZCB0byAwLjggYW5kIDEuMiBmcm9tIDAuNyBhbmQgMS41CiAgICAjIG51bWJlciBwb3MuIGVmZmVjdCBpbiBoaWdoZXIgb3JkZXIKICAgIG51bWJlcl9wb3NpdGl2ZV9leGNoYW5nZXMgPSBzdW0oLmRhdGFbWyJWYXJpYW50X3JhdGlvIl1dID4gMS4yNSksICMgYWRqdXN0ZWQgZnJvbSAxLjUgaW4gcGFwZXIgdG8gMS4yIGFjY29yZGluZyB0byBvYnNlcnZhdGlvbnMgZGV0YWlsZWQgYWJvdmUKICAgICMgbm8gZXBpc3Rhc2lzICgwLjcgPCB2YWx1ZSA8IDEuNSkKICAgIG51bWJlcl9ub19lcGlzdGFzaXMgPSBzdW0oLmRhdGFbWyJyYXRpb19XVF9oaWdoZXJPcmRlciJdXSA+IDAuNyAmIC5kYXRhW1sicmF0aW9fV1RfaGlnaGVyT3JkZXIiXV0gPCAxLjUpLAogICAgIyBwb3NpdGl2ZSBlcGlzdGFzaXMgKD4xLjUpCiAgICBudW1iZXJfcG9zaXRpdmVfZXBpc3Rhc2lzID0gc3VtKC5kYXRhW1sicmF0aW9fV1RfaGlnaGVyT3JkZXIiXV0gPiAxLjUpLAogICAgIyBuZWdhdGl2ZSBlcGlzdGFzaXMgKDwwLjcpCiAgICBudW1iZXJfbmVnYXRpdmVfZXBpc3Rhc2lzID0gc3VtKC5kYXRhW1sicmF0aW9fV1RfaGlnaGVyT3JkZXIiXV0gPCAwLjcpLAogICAgIyB0b3RhbCBoaWdoZXIgZXhjaGFuZ2UKICAgIG51bWJlcl9oaWdoZXJfZXhjaGFuZ2VzID0gbigpCiAgICApKQpgYGAKCmBgYHtyfQpleGNoYW5nZXNfZGF0YXNldCA8LSBzdW1tYXJpemVfZGF0YWZyYW1lWyxjKCJFeGNoYW5nZSIsICJDb25kaXRpb24iLCAibnVtYmVyX25lZ2F0aXZlX2V4Y2hhbmdlcyIsICJudW1iZXJfbmV1dHJhbF9leGNoYW5nZXMiLCAibnVtYmVyX3Bvc2l0aXZlX2V4Y2hhbmdlcyIpXQpleGNoYW5nZXNfZGF0YXNldCA8LSBwaXZvdF9sb25nZXIoZXhjaGFuZ2VzX2RhdGFzZXQsICFjKEV4Y2hhbmdlLCBDb25kaXRpb24pLCB2YWx1ZXNfdG89Im51bWJlciIsIG5hbWVzX3RvPSJleGNoYW5nZXMiKQoKZXhjaGFuZ2VzX2RhdGFzZXQkRXhjaGFuZ2UgPC0gZmFjdG9yKGV4Y2hhbmdlc19kYXRhc2V0JEV4Y2hhbmdlLCBsZXZlbHM9cmV2KGMoIkgxNDFMIiwgIkgxNDFWIiwgIk0xNTREIiwgIk0xNTRFIiwgIk0xNTRLIiwgIk0xNTRTIiwgIksyNjFBIiwgIksyNjFEIiwgIksyNjFFIiwgIksyNjFGIiwgIkgyNjVBIiwgIkgyNjVFIiwgIkgyNjVLIiwgIkgyNjVSIiwgIlYzMzdBIiwgIlYzMzdTIiwgIlE0MDZEIiwgIlE0MDZFIiwgIlM0NDZBIiwgIlM0NDZJIiwgIlM0NDZUIikpKQoKY29sb3JzX2V4Y2hhbmdlcyA8LSBicmV3ZXIucGFsKG4gPSA5LCBuYW1lID0gIkJsdWVzIilbYygzLDYsOSldCm5hbWVzKGNvbG9yc19leGNoYW5nZXMpIDwtIGMoIm51bWJlcl9wb3NpdGl2ZV9leGNoYW5nZXMiLCAibnVtYmVyX25ldXRyYWxfZXhjaGFuZ2VzIiwgIm51bWJlcl9uZWdhdGl2ZV9leGNoYW5nZXMiKQoKcCA8LSBnZ3Bsb3QoZXhjaGFuZ2VzX2RhdGFzZXQpICsKICBnZW9tX2JhcihhZXMoeCA9IG51bWJlciwgeSA9IEV4Y2hhbmdlLCBmaWxsID0gZXhjaGFuZ2VzKSwKICAgICAgICAgICBwb3NpdGlvbiA9ICJmaWxsIiwKICAgICAgICAgICBzdGF0ID0gImlkZW50aXR5IikgICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbG9yc19leGNoYW5nZXMpICsKICBmYWNldF9ncmlkKH4gQ29uZGl0aW9uLCBzd2l0Y2ggPSAieCIpICsgCiAgdGhlbWVfbGlnaHQoKSArIAogIHRoZW1lKHN0cmlwLnBsYWNlbWVudCA9ICJvdXRzaWRlIiwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIikpCnAgCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL2VwaXN0YXNpcy9Qb3NpdGl2ZV9uZWdhdGl2ZUV4Y2hhbmdlc19oaWdoZXJPcmRlcnZhcmlhbnRzLnBkZiIsIHBsb3Q9cCwgd2lkdGg9Ny41LCBoZWlnaHQ9MykKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvZXBpc3Rhc2lzL1Bvc2l0aXZlX25lZ2F0aXZlRXhjaGFuZ2VzX2hpZ2hlck9yZGVydmFyaWFudHMucG5nIiwgcGxvdD1wLCB3aWR0aD03LjUsIGhlaWdodD0zKQpgYGAKCmBgYHtyfQplcGlzdGFzaXNfZGF0YXNldCA8LSBzdW1tYXJpemVfZGF0YWZyYW1lWyxjKCJFeGNoYW5nZSIsICJDb25kaXRpb24iLCAibnVtYmVyX25vX2VwaXN0YXNpcyIsICJudW1iZXJfcG9zaXRpdmVfZXBpc3Rhc2lzIiwgIm51bWJlcl9uZWdhdGl2ZV9lcGlzdGFzaXMiKV0KZXBpc3Rhc2lzX2RhdGFzZXQgPC0gcGl2b3RfbG9uZ2VyKGVwaXN0YXNpc19kYXRhc2V0LCAhYyhFeGNoYW5nZSwgQ29uZGl0aW9uKSwgdmFsdWVzX3RvPSJudW1iZXIiLCBuYW1lc190bz0iZXBpc3Rhc2lzIikKCmVwaXN0YXNpc19kYXRhc2V0JEV4Y2hhbmdlIDwtIGZhY3RvcihlcGlzdGFzaXNfZGF0YXNldCRFeGNoYW5nZSwgbGV2ZWxzPXJldihjKCJIMTQxTCIsICJIMTQxViIsICJNMTU0RCIsICJNMTU0RSIsICJNMTU0SyIsICJNMTU0UyIsICJLMjYxQSIsICJLMjYxRCIsICJLMjYxRSIsICJLMjYxRiIsICJIMjY1QSIsICJIMjY1RSIsICJIMjY1SyIsICJIMjY1UiIsICJWMzM3QSIsICJWMzM3UyIsICJRNDA2RCIsICJRNDA2RSIsICJTNDQ2QSIsICJTNDQ2SSIsICJTNDQ2VCIpKSkKCmNvbG9yc19lcGlzdGFzaXMgPC0gYnJld2VyLnBhbChuID0gOSwgbmFtZSA9ICJCbHVlcyIpW2MoMyw2LDkpXQpuYW1lcyhjb2xvcnNfZXBpc3Rhc2lzKSA8LSBjKCJudW1iZXJfcG9zaXRpdmVfZXBpc3Rhc2lzIiwgIm51bWJlcl9ub19lcGlzdGFzaXMiLCAibnVtYmVyX25lZ2F0aXZlX2VwaXN0YXNpcyIpCgpwIDwtIGdncGxvdChlcGlzdGFzaXNfZGF0YXNldCkgKwogIGdlb21fYmFyKGFlcyh4ID0gbnVtYmVyLCB5ID0gRXhjaGFuZ2UsIGZpbGwgPSBlcGlzdGFzaXMpLAogICAgICAgICAgIHBvc2l0aW9uID0gImZpbGwiLAogICAgICAgICAgIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbG9yc19lcGlzdGFzaXMpICsKICBmYWNldF9ncmlkKH4gQ29uZGl0aW9uLCBzd2l0Y2ggPSAieCIpICsgCiAgdGhlbWVfbGlnaHQoKSArIAogIHRoZW1lKHN0cmlwLnBsYWNlbWVudCA9ICJvdXRzaWRlIiwKICAgICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSBOQSwgY29sb3IgPSAid2hpdGUiKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OCksIAogICAgICAgIGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gOCwgY29sb3VyID0gImJsYWNrIikpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvZXBpc3Rhc2lzL1Bvc2l0aXZlX25lZ2F0aXZlRXBpc3Rhc2lzX2hpZ2hlck9yZGVydmFyaWFudHMucGRmIiwgcGxvdD1wLCB3aWR0aD03LjUsIGhlaWdodD0zKQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9lcGlzdGFzaXMvUG9zaXRpdmVfbmVnYXRpdmVFcGlzdGFzaXNfaGlnaGVyT3JkZXJ2YXJpYW50cy5wbmciLCBwbG90PXAsIHdpZHRoPTcuNSwgaGVpZ2h0PTMpCmBgYAoKIyMgRXhhbXBsZXMgb2YgZXBpc3Rhc2lzCgpgYGB7cn0KZXBpc3RhdGljX3RhYmxlX3dpZGUgPC0gcGl2b3Rfd2lkZXIoZXBpc3RhdGljX3RhYmxlWyxjKCJDb25kaXRpb24iLCAiRXhjaGFuZ2VfdmFsdWVfYW5kX3JhdGlvX2luX1dUIiwgIkV4Y2hhbmdlIiwgIlZhcmlhbnQiLCAiVmFyaWFudF92YWx1ZSIsICJyYXRpb19XVF9oaWdoZXJPcmRlciIpXSwgbmFtZXNfZnJvbT1jKCJDb25kaXRpb24iKSwgdmFsdWVzX2Zyb209YygiVmFyaWFudF92YWx1ZSIsICJyYXRpb19XVF9oaWdoZXJPcmRlciIsICJFeGNoYW5nZV92YWx1ZV9hbmRfcmF0aW9faW5fV1QiKSkKZXBpc3RhdGljX3RhYmxlX3dpZGUgPC0gc3Vic2V0KGVwaXN0YXRpY190YWJsZV93aWRlLCBlcGlzdGF0aWNfdGFibGVfd2lkZSRyYXRpb19XVF9oaWdoZXJPcmRlcl9DTF9OMj4xLjUgJiBlcGlzdGF0aWNfdGFibGVfd2lkZSRyYXRpb19XVF9oaWdoZXJPcmRlcl9DTF9PMiA+IDEuNSAmIGVwaXN0YXRpY190YWJsZV93aWRlJHJhdGlvX1dUX2hpZ2hlck9yZGVyX0xEID4gMS41ICYgZXBpc3RhdGljX3RhYmxlX3dpZGUkVmFyaWFudF92YWx1ZV9DTF9OMiA+IDEuMCAmIGVwaXN0YXRpY190YWJsZV93aWRlJFZhcmlhbnRfdmFsdWVfQ0xfTzIgPiAxLjAgJiBlcGlzdGF0aWNfdGFibGVfd2lkZSRWYXJpYW50X3ZhbHVlX0xEID4gMS4wKQpucm93KGVwaXN0YXRpY190YWJsZV93aWRlKQplcGlzdGF0aWNfdGFibGVfd2lkZSRlcF9wcm9kdWN0IDwtIGVwaXN0YXRpY190YWJsZV93aWRlJHJhdGlvX1dUX2hpZ2hlck9yZGVyX0NMX04yICogZXBpc3RhdGljX3RhYmxlX3dpZGUkcmF0aW9fV1RfaGlnaGVyT3JkZXJfQ0xfTzIgKiBlcGlzdGF0aWNfdGFibGVfd2lkZSRyYXRpb19XVF9oaWdoZXJPcmRlcl9MRAplcGlzdGF0aWNfdGFibGVfd2lkZVtvcmRlcihlcGlzdGF0aWNfdGFibGVfd2lkZSRlcF9wcm9kdWN0LCBkZWNyZWFzaW5nPVRSVUUpLF0KYGBgCgpDaGVjayBhZ2FpbiBpZiBhbnkgb2YgdGhlICJnb29kLCByZWNvdmVyZWQiIHN0cmFpbnMgYXJlIGp1c3QgYW4gYXJ0ZWZhY3QgIQoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJNMTU0SyIsICJNMTU0SyxIMjY1RSxRNDA2RSxTNDQ2VCIsICJIMjY1RSxRNDA2RSxTNDQ2VCIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQp1bmlxdWUocGxvdF9zdWJzZXRbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJudW1fYmFyY29kZXMiKV0pCmRpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIk0xNTRLIj0iIzg4Y2NlZWZmIiwgIk0xNTRLLEgyNjVFLFE0MDZFLFM0NDZUIj0iIzExNzczM2ZmIiwgIkgyNjVFLFE0MDZFLFM0NDZUIj0iI2RkY2M3N2ZmIiwgIldUIj0iYmxhY2siLCBuZWdfY29udHJvbF9LMjE0PSJkYXJrZ3JheSIpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihwbG90X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJFcGlzdGF0aWMgZWZmZWN0cyBpbnZvbHZpbmcgTTE1NEsiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpwCgpwbG90X3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiSzI2MUQiLCAiSzI2MUQsSDI2NUUsUTQwNkUsUzQ0NlQiLCAiSDI2NUUsUTQwNkUsUzQ0NlQiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKdW5pcXVlKHBsb3Rfc3Vic2V0WyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iLCAibnVtX2JhcmNvZGVzIildKQpkaWZmZXJlbnRfY29sb3JzX3NldCA8LSBjKCJLMjYxRCI9IiM4OGNjZWVmZiIsICJLMjYxRCxIMjY1RSxRNDA2RSxTNDQ2VCI9IiMxMTc3MzNmZiIsICJIMjY1RSxRNDA2RSxTNDQ2VCI9IiNkZGNjNzdmZiIsICJXVCI9ImJsYWNrIiwgbmVnX2NvbnRyb2xfSzIxND0iZGFya2dyYXkiKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iRXBpc3RhdGljIGVmZmVjdHMgaW52b2x2aW5nIEsyNjFEIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkKcAoKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIlYzMzdBIiwgIkgxNDFWLE0xNTRBLEsyNjFGLEgyNjVBLFYzMzdBLFE0MDZFLFM0NDZJIiwgIkgxNDFWLE0xNTRBLEsyNjFGLEgyNjVBLFE0MDZFLFM0NDZJIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiVjMzN0EiPSIjODhjY2VlZmYiLCAiSDE0MVYsTTE1NEEsSzI2MUYsSDI2NUEsVjMzN0EsUTQwNkUsUzQ0NkkiPSIjMTE3NzMzZmYiLCAiSDE0MVYsTTE1NEEsSzI2MUYsSDI2NUEsUTQwNkUsUzQ0NkkiPSIjZGRjYzc3ZmYiLCAiV1QiPSJibGFjayIsIG5lZ19jb250cm9sX0syMTQ9ImRhcmtncmF5IikKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IkVwaXN0YXRpYyBlZmZlY3RzIGludm9sdmluZyBWMzM3QSIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpCnAKZ2dzYXZlKCIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvcGRmL2VwaXN0YXRpYy1lZmZlY3RzLVYzMzdBLnBkZiIsIHBsb3Q9cCwgd2lkdGg9MjAsIGhlaWdodD0yMCkKCnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJRNDA2RSIsICJIMTQxVixNMTU0QSxLMjYxRixIMjY1QSxWMzM3QSxRNDA2RSxTNDQ2SSIsICJIMTQxVixNMTU0QSxLMjYxRixIMjY1QSxWMzM3QSxTNDQ2SSIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQp1bmlxdWUocGxvdF9zdWJzZXRbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJudW1fYmFyY29kZXMiKV0pCmRpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIlE0MDZFIj0iIzg4Y2NlZWZmIiwgIkgxNDFWLE0xNTRBLEsyNjFGLEgyNjVBLFYzMzdBLFE0MDZFLFM0NDZJIj0iIzExNzczM2ZmIiwgIkgxNDFWLE0xNTRBLEsyNjFGLEgyNjVBLFYzMzdBLFM0NDZJIj0iI2RkY2M3N2ZmIiwgIldUIj0iYmxhY2siLCBuZWdfY29udHJvbF9LMjE0PSJkYXJrZ3JheSIpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihwbG90X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJFcGlzdGF0aWMgZWZmZWN0cyBpbnZvbHZpbmcgUTQwNkUiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi9lcGlzdGF0aWMtZWZmZWN0cy1RNDA2RS5wZGYiLCBwbG90PXAsIHdpZHRoPTIwLCBoZWlnaHQ9MjApCgpwbG90X3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiUTQwNkUiLCAiSDE0MVYsTTE1NEEsUTQwNkUiLCAiSDE0MVYsTTE1NEEiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKdW5pcXVlKHBsb3Rfc3Vic2V0WyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iLCAibnVtX2JhcmNvZGVzIildKQpkaWZmZXJlbnRfY29sb3JzX3NldCA8LSBjKCJRNDA2RSI9IiM4OGNjZWVmZiIsICJIMTQxVixNMTU0QSxRNDA2RSI9IiMxMTc3MzNmZiIsICJIMTQxVixNMTU0QSI9IiNkZGNjNzdmZiIsICJXVCI9ImJsYWNrIiwgbmVnX2NvbnRyb2xfSzIxND0iZGFya2dyYXkiKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iRXBpc3RhdGljIGVmZmVjdHMgaW52b2x2aW5nIFE0MDZFIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkKcAoKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIksyNjFBIiwgIksyNjFBLEgyNjVLLFE0MDZFLFM0NDZBIiwgIkgyNjVLLFE0MDZFLFM0NDZBIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiSzI2MUEiPSIjODhjY2VlZmYiLCAiSzI2MUEsSDI2NUssUTQwNkUsUzQ0NkEiPSIjMTE3NzMzZmYiLCAiSDI2NUssUTQwNkUsUzQ0NkEiPSIjZGRjYzc3ZmYiLCAiV1QiPSJibGFjayIsIG5lZ19jb250cm9sX0syMTQ9ImRhcmtncmF5IikKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IkVwaXN0YXRpYyBlZmZlY3RzIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkKcAoKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkgyNjVBIiwgIk0xNTRBLEsyNjFBLEgyNjVBLFE0MDZEIiwgIk0xNTRBLEsyNjFBLFE0MDZEIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiSDI2NUEiPSIjODhjY2VlZmYiLCAiTTE1NEEsSzI2MUEsSDI2NUEsUTQwNkQiPSIjMTE3NzMzZmYiLCAiTTE1NEEsSzI2MUEsUTQwNkQiPSIjZGRjYzc3ZmYiLCAiV1QiPSJibGFjayIsIG5lZ19jb250cm9sX0syMTQ9ImRhcmtncmF5IikKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IkVwaXN0YXRpYyBlZmZlY3RzIGludm9sdmluZyBIMjY1QSIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpCnAKCnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJTNDQ2VCIsICJNMTU0QSxLMjYxQSxRNDA2RCxTNDQ2VCIsICJNMTU0QSxLMjYxQSxRNDA2RCIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQp1bmlxdWUocGxvdF9zdWJzZXRbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJudW1fYmFyY29kZXMiKV0pCmRpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIlM0NDZUIj0iIzg4Y2NlZWZmIiwgIk0xNTRBLEsyNjFBLFE0MDZELFM0NDZUIj0iIzExNzczM2ZmIiwgIk0xNTRBLEsyNjFBLFE0MDZEIj0iI2RkY2M3N2ZmIiwgIldUIj0iYmxhY2siLCBuZWdfY29udHJvbF9LMjE0PSJkYXJrZ3JheSIpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihwbG90X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJFcGlzdGF0aWMgZWZmZWN0cyBpbnZvbHZpbmcgUzQ0NlQiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpwCgpwbG90X3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiSzI2MUYiLCAiSzI2MUYsSDI2NUssUTQwNkQsUzQ0NkkiLCAiSDI2NUssUTQwNkQsUzQ0NkkiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKdW5pcXVlKHBsb3Rfc3Vic2V0WyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iLCAibnVtX2JhcmNvZGVzIildKQpkaWZmZXJlbnRfY29sb3JzX3NldCA8LSBjKCJLMjYxRiI9IiM4OGNjZWVmZiIsICJLMjYxRixIMjY1SyxRNDA2RCxTNDQ2SSI9IiMxMTc3MzNmZiIsICJIMjY1SyxRNDA2RCxTNDQ2SSI9IiNkZGNjNzdmZiIsICJXVCI9ImJsYWNrIiwgbmVnX2NvbnRyb2xfSzIxND0iZGFya2dyYXkiKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iRXBpc3RhdGljIGVmZmVjdHMgaW52b2x2aW5nIEsyNjFGIikgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkKcAoKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkgyNjVFIiwgIksyNjFELEgyNjVFLFE0MDZFLFM0NDZUIiwgIksyNjFELFE0MDZFLFM0NDZUIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiSDI2NUUiPSIjODhjY2VlZmYiLCAiSzI2MUQsSDI2NUUsUTQwNkUsUzQ0NlQiPSIjMTE3NzMzZmYiLCAiSzI2MUQsUTQwNkUsUzQ0NlQiPSIjZGRjYzc3ZmYiLCAiV1QiPSJibGFjayIsIG5lZ19jb250cm9sX0syMTQ9ImRhcmtncmF5IikKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IkVwaXN0YXRpYyBlZmZlY3RzIGludm9sdmluZyBIMjY1RSIpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpCnAKCnBsb3Rfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJLMjYxQSIsICJLMjYxQSxIMjY1SyxRNDA2RSxTNDQ2QSIsICJIMjY1SyxRNDA2RSxTNDQ2QSIsICJXVCIsIG5lZ19jb250cm9sX0syMTQpKQp1bmlxdWUocGxvdF9zdWJzZXRbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgImNvbmRpdGlvbiIsICJudW1fYmFyY29kZXMiKV0pCmRpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIksyNjFBIj0iIzg4Y2NlZWZmIiwgIksyNjFBLEgyNjVLLFE0MDZFLFM0NDZBIj0iIzExNzczM2ZmIiwgIkgyNjVLLFE0MDZFLFM0NDZBIj0iI2RkY2M3N2ZmIiwgIldUIj0iYmxhY2siLCBuZWdfY29udHJvbF9LMjE0PSJkYXJrZ3JheSIpCnAgPC0gbGluZXBsb3RfQ1ZpbnRlcnZhbF9zZXZlcmFsQ29sb3Vyc19tZWFubG9nMihwbG90X3N1YnNldCkgKyBsYWJzKHRpdGxlPSJFcGlzdGF0aWMgZWZmZWN0cyBpbnZvbHZpbmcgSzI2MUEiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpwCmBgYAoKIyBWYXJpYW50cyBmb3Igd2hpY2ggbXV0YW50IHN0cmFpbnMgZXhpc3QKCiJ2YXJpYW50IEgxNDFWLEsyNjFBLEgyNjVLLFE0MDZFLFM0NDZJIHdpdGggdGhlIGhpZ2hlc3QgZml0bmVzcyB2YWx1ZSBpbiB0aGUgbGlicmFyeSwgYnV0IHNoaXR0eSBhZGp1c3RlZCBwIHZhbHVlLCBpcyBIMTQxVixLMjYxQSxIMjY1SyxRNDA2RSxTNDQ2SSAobm9ybT0yLjE4LCBwLmFkaj0wLjEwNyksIGFsdGVybmF0aXZlIG9wdGlvbiBmb3Igc2hvd2luZyBlcGlzdGFzaXMgKGJlc2lkZXMgRCBhbmQgRSksIG5lZWRzIGNvbnRyb2wgc3RyYWlucyBIMTQxViwgSzI2MUEsIFE0MDZFLCBTNDQ2SSBhbmQgSDI2NUsgdG8gc2hvdyB0aGF0IEgyNjVLIGlzIGRldHJpbWVudGFsIHdoZW4gaW50cm9kdWNlZCBvbiBpdHMgb3duLCBiZW5lZmljaWFsIGluIGJhY2tncm91bmQgb2YgcXVhcmRydXBsZSBtdXRhbnQiCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkgxNDFWLEsyNjFBLEgyNjVLLFE0MDZFLFM0NDZJIiwgIkgxNDFWLEsyNjFBLFE0MDZFLFM0NDZJIiwgIkgyNjVLIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IkVwaXN0YXRpYyBlZmZlY3RzIGFuZCAnc3RyYWluIEEnIikKcApgYGAKCkgxNDFWLCBNMTU0QSwgSzI2MUEsIEgyNjVBLCBRNDA2RSwgUzQ0NlQJb3RoZXIgYWx0ZXJuYXRpdmUgb3B0aW9uIGZvciBzaG93aW5nIGVwaXN0YXNpcyAoYmVzaWRlcyBFKSwgdGhlcmUgc2hvdWxkIGFsc28gYmUgYSBjb3JyZXNwb25kaW5nIG11dGFudCB3aXRoIDUgZXhjaGFuZ2VzIChIMTQxViwgTTE1NEEsIEsyNjFBLCBRNDA2RSwgUzQ0NlQpIGFuZCB0aGVuIHRoZSBjb21wYXJpc29uIHRoYXQgSDI2NUEgaXMgZGV0cmltZW50YWwgb24gaXRzIG93biwgYnV0IGJlbmVmaWNpYWwgaW4gdGhlIGJhY2tncm91bmQgb2YgdGhlIG11dGFudCB3aXRoIDUgZXhjaGFnbmVzCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpwbG90X3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiSDE0MVYsTTE1NEEsSzI2MUEsSDI2NUEsUTQwNkUsUzQ0NlQiLCAiSDE0MVYsTTE1NEEsSzI2MUEsUTQwNkUsUzQ0NlQiLCAiSDI2NUEiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKdW5pcXVlKHBsb3Rfc3Vic2V0WyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iLCAibnVtX2JhcmNvZGVzIildKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iRXBpc3RhdGljIGVmZmVjdHMgYW5kICdzdHJhaW4gRCciKQpwCmBgYAoKSDE0MVYsIEsyNjFFLCBIMjY1QSwgUzQ0NkEJImZpdHRlc3QgY29tYmluYXRvcmlhbCBtdXRhbnQgdmFyaWFudCB3aXRoIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBoYXMgZm91cgphbWlubyBhY2lkIGV4Y2hhbmdlczogSDE0MVYsSzI2MUUsSDI2NUEsUzQ0NkEgKG5vcm09MS41NiwgcC5hZGo9MC4wMjU5KQosIG1haW4gbGluZSBGaWd1cmUgc2hvd2luZyBlcGlzdGFzaXMsIHRoZXJlIHNob3VsZCBhbHNvIGJlIGEgY29ycmVzcG9uZGluZyB0cmlwbGUgbXV0YW50IChIMTQxViwgSzI2MUUsIFM0NDZBKSBzaG93aW5nIHRoYXQgSDI2NUEgb24gaXRzIG93biBpcyBkZXRyaW1lbnRhbCwgaW4gYmFja2dyb3VuZCBvZiBIMTQxViwgSzI2MUUsIFM0NDZBIGJlbmVmaWNpYWwiCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkgxNDFWLEsyNjFFLEgyNjVBLFM0NDZBIiwgIkgxNDFWLEsyNjFFLFM0NDZBIiwgIkgyNjVBIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IkVwaXN0YXRpYyBlZmZlY3RzIGFuZCAnc3RyYWluIEUnIikKcApgYGAKClZhcmlhbnQgdW5kZXJseWluZyB0aGUgc3VnZ2VzdGVkIGhpZ2hlci1vcmRlciB2YXJpYW50czogSDE0MVYsIEsyNjFBLCBIMjY1QSwgUTQwNkUsIFM0NDZUCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KZGlmZmVyZW50X2NvbG9yc19zZXQgPC0gYygiVzEyOEkiPSIjODhjY2VlZmYiLCAiSDE0MVYsSzI2MUEsSDI2NUEsUTQwNkUsUzQ0NlQiPSIjMTE3NzMzZmYiLCAiSzM2MEkiPSIjZGRjYzc3ZmYiLCAiV1QiPSJibGFjayIsIG5lZ19jb250cm9sX0syMTQ9ImRhcmtncmF5IikKcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIkgxNDFWLEsyNjFBLEgyNjVBLFE0MDZFLFM0NDZUIiwgIlcxMjhJIiwgIkszNjBJIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IlZhcmlhbnQgdW5kZXJseWluZyBCLCBDLCBCZml4IGFuZCBzaW5nbGUgZXhjaGFuZ2VzIGludHJvZHVjZWQgaW4gdGhlc2UgdmFyaWFudHMiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1kaWZmZXJlbnRfY29sb3JzX3NldCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpwCmdnc2F2ZSgiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3BkZi92YXJpYW50X3VuZGVybHlpbmdfQi1DLUJmaXgucGRmIiwgcGxvdD1wLCB3aWR0aD0yMCwgaGVpZ2h0PTIwKQpgYGAKClcxMjhJCXNpbmdsZSBleGNoYW5nZSB3aXRoIGhpZ2ggZml0bmVzcyB2YWx1ZSwgSDM1OFMJc2luZ2xlIGV4Y2hhbmdlIHdpdGggaGlnaCBmaXRuZXNzIHZhbHVlLCBLMzYwSQlzaW5nbGUgZXhjaGFuZ2Ugd2l0aCBoaWdoIGZpdG5lc3MgdmFsdWUKCjwhLS0gU2luZ2xlIGFtaW5vIGFjaWQgZXhjaGFuZ2UgdmFyaWFudHMgd2l0aCBhIGhpZ2ggbm9ybWFsaXplZCBmaXRuZXNzIHNjb3JlIGFuZCBhIGxvdyBhZGp1c3RlZCBwIHZhbHVlIGFyZSBXMTI4SSAobm9ybT0xLjY3LCBwLmFkaj0wLjAwMiksIEgzNThTIChub3JtPTEuNywgcC5hZGo9MC4wMDAwMSkgYW5kIEszNjBJIChub3JtPTEuNjYsIHAuYWRqPTAuMDAwMDAwOTM3KS4gLS0+CgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIlcxMjhJIiwgIkgzNThTIiwgIkszNjBJIiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCnVuaXF1ZShwbG90X3N1YnNldFssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgIm51bV9iYXJjb2RlcyIpXSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKHBsb3Rfc3Vic2V0KSArIGxhYnModGl0bGU9IlNpbmdsZSBleGNoYW5nZXMgYWRkZWQgY2xvbmVkIGFzIHNpbmdsZSBtdXRhbnQgc3RyYWlucyIpCnAKYGBgCgpIaWdoZXIgb3JkZXIgdmFyaWFudCB3aXRoIFY5OEQsIEYxMDRDLCBXMTI4SywgUTE0MkQsIEsyMzNJLCBWMjcwTCwgTTM0NUksIEgzNThTCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KcGxvdF9zdWJzZXQgPC0gc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJHNnUk5BX3RhcmdldCAlaW4lIGMoIlY5OEQiLCAiRjEwNEMiLCAiVzEyOEsiLCAiUTE0MkQiLCAiSzIzM0kiLCAiVjI3MEwiLCAiTTM0NUkiLCAiSDM1OFMiLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKdW5pcXVlKHBsb3Rfc3Vic2V0WyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iLCAibnVtX2JhcmNvZGVzIildKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIocGxvdF9zdWJzZXQpICsgbGFicyh0aXRsZT0iU2luZ2xlIGV4Y2hhbmdlcyB1bmRlcmx5aW5nIGhpZ2hlciBvcmRlciB2YXJpYW50ICgnTTgnKSIpCnAKYGBgCgojIE90aGVyIGFuYWx5c2VzIGZvciBjb21iaW5hdG9yaWFsIGxpYnJhcnkKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpzaW5nbGVfbXV0c19jb21iaW5hdG9yaWFsIDwtIHBpdm90X3dpZGVyKHVuaXF1ZShzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkY2F0ZWdvcnk9PSJjb21iaUFORHNhdHVyIilbLGNvbHVtbnNdKSwgbmFtZXNfZnJvbT1jb25kaXRpb24sIHZhbHVlc19mcm9tPWMobm9ybSwgcF9maXRfYWRqX1dUKSkKc2luZ2xlX211dHNfY29tYmluYXRvcmlhbCRub3JtX211bHRpcGx5IDwtIHNpbmdsZV9tdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9DTF9OMiAqIHNpbmdsZV9tdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9DTF9PMiAqIHNpbmdsZV9tdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9MRAp3cml0ZS5jc3Yoc2luZ2xlX211dHNfY29tYmluYXRvcmlhbCwgZmlsZT0iLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3NpbmdsZV9zaXRlX2V4Y2hhbmdlc19jb21iaW5hdG9yaWFsTGlicmFyeS5jc3YiKQpzaW5nbGVfbXV0c19jb21iaW5hdG9yaWFsW29yZGVyKHNpbmdsZV9tdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9tdWx0aXBseSwgZGVjcmVhc2luZz1UUlVFKSxjKDEsNCw1LDYsMTAsMiwzLDcsOCw5KV0KYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5ncz1GQUxTRX0KY29tYmlfc2luZ2xlX3N1YnNldCA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkc2dSTkFfdGFyZ2V0ICVpbiUgYygiV1QiLCAiSDI2NUsiLCAiSDI2NVIiLCAiVjMzN0EiLCAiVjMzN1MiLCBuZWdfY29udHJvbF9LMjE0KSkKcCA8LSBsaW5lcGxvdF9DVmludGVydmFsX3NldmVyYWxDb2xvdXJzX21lYW5sb2cyKGNvbWJpX3NpbmdsZV9zdWJzZXQpICsgbGFicyh0aXRsZT0iTW9zdCBkZXRyaW1lbnRhbCBzaW5nbGUgdmFyaWFudHMgaW4gY29tYmluYXRvcmlhbCBsaWJyYXJ5IikKcApgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmdzPUZBTFNFfQpmb3Iobl9tdXQgaW4gMjo3KXsKICBwcmludChuX211dCkKICBtdXRzX2NvbWJpbmF0b3JpYWwgPC0gcGl2b3Rfd2lkZXIodW5pcXVlKHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRudW1iZXJfbXV0cz09bl9tdXQpWyxjb2x1bW5zXSksIG5hbWVzX2Zyb209Y29uZGl0aW9uLCB2YWx1ZXNfZnJvbT1jKG5vcm0sIHBfZml0X2Fkal9XVCkpCiAgbXV0c19jb21iaW5hdG9yaWFsJG5vcm1fbXVsdGlwbHkgPC0gbXV0c19jb21iaW5hdG9yaWFsJG5vcm1fQ0xfTjIgKiBtdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9DTF9PMiAqIG11dHNfY29tYmluYXRvcmlhbCRub3JtX0xECiAgcHJpbnQoIlNvcnRlZCBhY2NvcmRpbmcgdG8gcHJvZHVjdCBvZiBkaWZmZXJlbnQgbm9ybWVkIGZpdG5lc3MgdmFsdWVzIikKICBwcmludChoZWFkKG11dHNfY29tYmluYXRvcmlhbFtvcmRlcihtdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9tdWx0aXBseSwgZGVjcmVhc2luZz1UUlVFKSxjKDEsNCw1LDYsMTAsMiwzLDcsOCw5KV0pKQogIG11dHNfY29tYmluYXRvcmlhbCA8LSBzdWJzZXQobXV0c19jb21iaW5hdG9yaWFsLCBtdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9DTF9OMiA+IDEuMCAmIG11dHNfY29tYmluYXRvcmlhbCRub3JtX0NMX08yID4gMS4wICYgbXV0c19jb21iaW5hdG9yaWFsJG5vcm1fTEQgPiAxLjApCiAgbXV0c19jb21iaW5hdG9yaWFsIDwtIHN1YnNldChtdXRzX2NvbWJpbmF0b3JpYWwsIChtdXRzX2NvbWJpbmF0b3JpYWwkcF9maXRfYWRqX1dUX0NMX04yIDwgMC4wNSB8IG11dHNfY29tYmluYXRvcmlhbCRwX2ZpdF9hZGpfV1RfQ0xfTzIgPCAwLjA1IHwgbXV0c19jb21iaW5hdG9yaWFsJHBfZml0X2Fkal9XVF9MRCA8IDAuMDUpICYgbXV0c19jb21iaW5hdG9yaWFsJG5vcm1fQ0xfTjIgPiAxLjAgJiBtdXRzX2NvbWJpbmF0b3JpYWwkbm9ybV9DTF9PMiA+IDEuMCAmIG11dHNfY29tYmluYXRvcmlhbCRub3JtX0xEID4gMS4wKSAKICBwcmludCgiT25seSBzZWxlY3RpbmcgZm9yIHZhcmlhbnRzIHdpdGggYSBub3JtZWQgZml0bmVzcyB2YWx1ZSBhYm92ZSAxIGluIGFsbCBjb25kaXRpb25zIGFuZCBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgdG8gdGhlIGJhc2UgdmFyaWFudCBpbiBhdCBsZWFzdCBvbmUgb2YgdGhlIGNvbmRpdGlvbnMiKQogIHByaW50KGhlYWQobXV0c19jb21iaW5hdG9yaWFsW29yZGVyKG11dHNfY29tYmluYXRvcmlhbCRub3JtX211bHRpcGx5LCBkZWNyZWFzaW5nPVRSVUUpLGMoMSw0LDUsNiwxMCwyLDMsNyw4LDkpXSkpCn0KCmBgYAojIFRydW5jYXRlZCB2YXJpYW50cwoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZ3M9RkFMU0V9CnRydW5jYXRlZF92YXJpYW50cyA8LSBzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkY2F0ZWdvcnkgPT0ibm90RXhwZWN0ZWQiKQpsZW5ndGgodW5pcXVlKHRydW5jYXRlZF92YXJpYW50cyRzZ1JOQV90YXJnZXQpKQp0cnVuY2F0ZWRfd2lkZSA8LSBwaXZvdF93aWRlcih1bmlxdWUodHJ1bmNhdGVkX3ZhcmlhbnRzWyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJjb25kaXRpb24iLCAicF9maXRfYWRqX1dUIiwgIm51bV9iYXJjb2RlcyIpXSksIHZhbHVlc19mcm9tID1jKG5vcm0scF9maXRfYWRqX1dUKSwgbmFtZXNfZnJvbT1jb25kaXRpb24pCnRydW5jYXRlZF93aWRlJG5vcm1fcHJvZHVjdCA8LSB0cnVuY2F0ZWRfd2lkZSRub3JtX0NMX04yICogdHJ1bmNhdGVkX3dpZGUkbm9ybV9DTF9PMiAqIHRydW5jYXRlZF93aWRlJG5vcm1fTEQKdHJ1bmNhdGVkX3dpZGVbb3JkZXIodHJ1bmNhdGVkX3dpZGUkbm9ybV9wcm9kdWN0LCBkZWNyZWFzaW5nPVRSVUUpLF0KdHJ1bmNhdGVkX3N1YnNldCA8LSBjb21iaV9zaW5nbGVfc3Vic2V0IDwtIHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRzZ1JOQV90YXJnZXQgJWluJSBjKCJHMzk1VixTVE9QMzk1IiwgIkY0MDVTLFE0MDZSLE40MDdULFNUT1A0MDciLCAiV1QiLCBuZWdfY29udHJvbF9LMjE0KSkKdHJ1bmNhdGVkX3N1YnNldCRzZ1JOQV90YXJnZXQgPC0gZmFjdG9yKHRydW5jYXRlZF9zdWJzZXQkc2dSTkFfdGFyZ2V0LCBsZXZlbHM9YygiRzM5NVYsU1RPUDM5NSIsICJGNDA1UyxRNDA2UixONDA3VCxTVE9QNDA3IiwgIldUIiwgbmVnX2NvbnRyb2xfSzIxNCkpCmRpZmZlcmVudF9jb2xvcnNfc2V0IDwtIGMoIkczOTVWLFNUT1AzOTUiPSIjNDRhYTk5ZmYiLCAiRjQwNVMsUTQwNlIsTjQwN1QsU1RPUDQwNyI9IiNhYTQ0OTlmZiIsICJXVCI9ImJsYWNrIiwgIksyMTRSIj0iZGFya2dyYXkiKQpkaWZmZXJlbnRfbGluZXR5cGVzX3NldCA8LSBjKCJHMzk1VixTVE9QMzk1Ij00NCwgIkY0MDVTLFE0MDZSLE40MDdULFNUT1A0MDciPTEzNDMsICJXVCI9InNvbGlkIiwgIksyMTRSIj0ibG9uZ2Rhc2giKQpwIDwtIGxpbmVwbG90X0NWaW50ZXJ2YWxfc2V2ZXJhbENvbG91cnNfbWVhbmxvZzIodHJ1bmNhdGVkX3N1YnNldCkgKyBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9saW5ldHlwZXNfc2V0KSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9ZGlmZmVyZW50X2NvbG9yc19zZXQpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWRpZmZlcmVudF9jb2xvcnNfc2V0KQpnZ3NhdmUoIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9wZGYvdHJ1bmNhdGVkX3ZhcmlhbnRzX3BlcmZvcm1pbmdfd2VsbC5wZGYiLCBwbG90PXAsIGhlaWdodD0xLjc1LCB3aWR0aD02LCB1bml0cz0iaW4iKQpwCmBgYAoKIyBQcmludCB0YWJsZXMgd2l0aCBtdXRhdGlvbmFsIGVmZmVjdCBvZiBzaW5nbGUgYW1pbm8gYWNpZCBleGNoYW5nZXMKCmBgYHtyfQpzaW5nbGVfbXV0cyA8LSB1bmlxdWUoc3Vic2V0KGZpdG5lc3NfZGF0YSwgZml0bmVzc19kYXRhJG51bWJlcl9tdXRzPT0xICYgZml0bmVzc19kYXRhJGNvbmRpdGlvbj09IkNMX04yIilbLGMoInNnUk5BX3RhcmdldCIsICJub3JtIiwgInNkX2ZpdG5lc3MiLCAicF9maXRfYWRqX1dUIiwgIm51bV9iYXJjb2RlcyIpXSkKd3JpdGVfY3N2KHNpbmdsZV9tdXRzLCAiLi4vbGZjU0Vfd2VpZ2h0ZWRfb3V0cHV0SW1hZ2VzL3N2Zy9GaWcxL2hlYXRNYXAvc2luZ2xlX211dHNfQ0xOMi5jc3YiKQoKc2luZ2xlX211dHMgPC0gdW5pcXVlKHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRudW1iZXJfbXV0cz09MSAmIGZpdG5lc3NfZGF0YSRjb25kaXRpb249PSJDTF9PMiIpWyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJzZF9maXRuZXNzIiwgInBfZml0X2Fkal9XVCIsICJudW1fYmFyY29kZXMiKV0pCndyaXRlX2NzdihzaW5nbGVfbXV0cywgIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9zdmcvRmlnMS9oZWF0TWFwL3NpbmdsZV9tdXRzX0NMTzIuY3N2IikKCnNpbmdsZV9tdXRzIDwtIHVuaXF1ZShzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkbnVtYmVyX211dHM9PTEgJiBmaXRuZXNzX2RhdGEkY29uZGl0aW9uPT0iTEQiKVssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAic2RfZml0bmVzcyIsICJwX2ZpdF9hZGpfV1QiLCAibnVtX2JhcmNvZGVzIildKQp3cml0ZV9jc3Yoc2luZ2xlX211dHMsICIuLi9sZmNTRV93ZWlnaHRlZF9vdXRwdXRJbWFnZXMvc3ZnL0ZpZzEvaGVhdE1hcC9zaW5nbGVfbXV0c19MRC5jc3YiKQpgYGAKCiMgQ3JlYXRlIGZpbGVzIGZvciBQcm90ZWluTlBUCgpDb21wYXJlIGh0dHBzOi8vZ2l0aHViLmNvbS9PQVRNTC1NYXJrc2xhYi9Qcm90ZWluTlBULiBFeHBlY3RzIGEgLmNzdiBmaWxlLiAiQXQgYSBtaW5pbXVtIHRoaXMgZmlsZSByZXF1aXJlcyAyIGZpZWxkczogbXV0YXRlZF9zZXF1ZW5jZSAoZnVsbCBzZXF1ZW5jZSBvZiBhbWlubyBhY2lkcykgYW5kIERNU19zY29yZSAoYXNzYXkgbWVhc3VyZW1lbnQpLiBJZiBubyBmb2xkIHZhcmlhYmxlIGlzIGluY2x1ZGVkIGluIHRoZSBhc3NheSBmaWxlLCB0aGUgcGlwZWxpbmUgc2NyaXB0IHdpbGwgYXV0b21hdGljYWxseSBjcmVhdGUgYSBmb2xkX3JhbmRvbV81IHZhcmlhYmxlLCBhc3NpZ25pbmcgZWFjaCBtdXRhbnQgdG8gZm9sZHMgMC00IGF0IHJhbmRvbS4gWW91IG1heSBhbHNvIHVzZSB5b3VyIG93biBjcm9zcy12YWxpZGF0aW9uIHNjaGVtZSAoZWcuLCBhc3NpZ24gYWxsIHRyYWluaW5nIHNlcXVlbmNlcyB0byBmb2xkIDAsIGFsbCB0ZXN0IHNlcXVlbmNlcyB0byBmb2xkIDEpLiBUbyB0aGF0IGVuZCwgeW91IG9ubHkgbmVlZCB0byBwYXNzIHRvIHRoZSBwaXBlbGluZSBzY3JpcHQgdGhlIG5hbWUgb2YgdGhhdCBmb2xkIHZhcmlhYmxlIHZpYSB0aGUgZm9sZF92YXJpYWJsZV9uYW1lIGFyZ3VtZW50IGFuZCBzcGVjaWZ5IHRoZSBpbmRleCBvZiB0aGUgdGVzdCBmb2xkIHZpYSB0aGUgdGVzdF9mb2xkX2luZGV4IGFyZ3VtZW50IChpZiB0ZXN0X2ZvbGRfaW5kZXggaXMgbm90IHBhc3NlZCBhcyBhcmd1bWVudCwgdGhlIHNjcmlwdCB3aWxsIGF1dG9tYXRpY2FsbHkgcGVyZm9ybSBhIGZ1bGwgY3Jvc3MtdmFsaWRhdGlvbiwgcm90YXRpbmcgdGhlIHRlc3QgZm9sZCBpbmRleCBhdCBlYWNoIGl0ZXJhdGlvbikuIgoKQ3JlYXRlIGEgZmlsZSBmb3IgCmEpIHByZWRpY3RpbmcgaGlnaGVyLW9yZGVyIGNvbWJpbmF0aW9ucwotIHNlcGFyYXRlIGFtaW5vIGFjaWQgcG9zaXRpb25zIGludG8gNSBmb2xkcyAtLT4gYWRkIGZvbGRzIHVzaW5nIHB5dGhvbgotIHRocmVlIGZpbGVzIGZvciBlYWNoIGNvbmRpdGlvbgoKVXNlIHB5dGhvbiBzY3JpcHQgc2luZ2xlX211dHNfdHJhaW5fZm9yX2NvbWJpLnB5IHRvIGFzc2lnbiBmb2xkcyBmb3IgdHJhaW5pbmcgZXRjLiwgb3V0cHV0IHNhdHVyYXRpb25hbF9wcm90ZWluTlBUX3dpdGhfZm9sZENoYW5nZS5jc3YKClJlZ2FyZGluZyBmb2xkczogIldlIGRldmVsb3AgMyBkaXN0aW5jdCBjcm9zcy12YWxpZGF0aW9uIHNjaGVtZXMgdG8gYXNzZXNzIHRoZSBhYmlsaXR5IG9mIGVhY2ggbW9kZWwgdG8gZXh0cmFwb2xhdGUgdG8gcG9zaXRpb25zIG5vdCBlbmNvdW50ZXJlZCBkdXJpbmcgdHJhaW5pbmcuIEluIHRoZSBSYW5kb20gc2NoZW1lLCBjb21tb25seS11c2VkIGluIG90aGVyIHN1cGVydmlzZWQgZml0bmVzcyBwcmVkaWN0aW9uIGJlbmNobWFya3MgW1JhbyBldCBhbC4sIDIwMTksIERhbGxhZ28gZXQgYWwuLCAyMDIyXSwgZWFjaCBtdXRhdGlvbiBpcyByYW5kb21seSBhbGxvY2F0ZWQgdG8gb25lIG9mIGZpdmUgZGlzdGluY3QgZm9sZHMuIEluIHRoZSBDb250aWd1b3VzIHNjaGVtZSwgdGhlIHNlcXVlbmNlIGlzIHNwbGl0IGludG8gZml2ZSBjb250aWd1b3VzIHNlZ21lbnRzIGFsb25nIGl0cyBsZW5ndGgsIHdpdGggbXV0YXRpb25zIGFzc2lnbmVkIHRvIGVhY2ggc2VnbWVudCBiYXNlZCBvbiB0aGUgcG9zaXRpb24gdGhleSBvY2N1ciBpbiB0aGUgc2VxdWVuY2UuIExhc3RseSwgdGhlIE1vZHVsbyBzY2hlbWUgdXNlcyB0aGUgbW9kdWxvIG9wZXJhdG9yIHRvIGFzc2lnbiBtdXRhdGVkIHBvc2l0aW9ucyB0byBlYWNoIGZvbGQuIEZvciBleGFtcGxlLCBwb3NpdGlvbiAxIGlzIGFzc2lnbmVkIHRvIGZvbGQgMSwgcG9zaXRpb24gMiB0byBmb2xkIDIsIGFuZCBzbyBvbiwgbG9vcGluZyBiYWNrIHRvIGZvbGQgMSBhdCBwb3NpdGlvbiA2LiBUaGlzIHBhdHRlcm4gY29udGludWVzIHRocm91Z2hvdXQgdGhlIHNlcXVlbmNlLiBXZSBub3RlIHRoYXQgdGhlcmUgaXMgbm8gaW5oZXJlbnQgaXNzdWUgd2l0aCB1c2luZyBhIFJhbmRvbSBjcm9zcy12YWxpZGF0aW9uIHNjaGVtZSB0byBlc3RpbWF0ZSB0aGUgcGVyZm9ybWFuY2Ugb2YgcHJlZGljdGl2ZSBtb2RlbHMuIEhvd2V2ZXIsIHRoZSBjb25jbHVzaW9ucyBkcmF3biBhbmQgdGhlIGdlbmVyYWxpemFiaWxpdHkgY2xhaW1zIGJhc2VkIG9uIGl0IHJlcXVpcmUgY2FyZWZ1bCBjb25zaWRlcmF0aW9uLiIgLS0+IHVzZSBtb2R1bG8gc2NoZW1lIGZvciBhc3NpZ25pbmcgZm9sZHMgdG8gc2F0dXJhdGlvbmFsIGxpYnJhcnkKCmIpIHByZWRpY3RpbmcgbW9yZSBjb21iaW5hdGlvbnMKLSBob3cgdG8gYXNzaWduIGZvbGRzPyB0YWtlIG1vZHVsbyBmb2xkcyBmcm9tIHNhdHVyYXRpb25hbCwgY29tYmluZSBjb21iaW5hdG9yaWFsIHBhcnQgYW5kIHNhdHVyYXRpb25hbCBwYXJ0IGluIHRoZSBlbmQKCgpgYGB7cn0Kc2F0dXJhdGlvbmFsX2Zvcl9wcm90ZWluTlBUIDwtIHVuaXF1ZShzdWJzZXQoZml0bmVzc19kYXRhLCBmaXRuZXNzX2RhdGEkbnVtYmVyX211dHM9PTEpWyxjKCJzZ1JOQV90YXJnZXQiLCAibm9ybSIsICJiYXNlQUEiLCAiY29uZGl0aW9uIildKQpzYXR1cmF0aW9uYWxfZm9yX3Byb3RlaW5OUFQgPC0gcGl2b3Rfd2lkZXIoc2F0dXJhdGlvbmFsX2Zvcl9wcm90ZWluTlBULCBuYW1lc19mcm9tPWNvbmRpdGlvbiwgdmFsdWVzX2Zyb209bm9ybSkKd3JpdGVfY3N2KHNhdHVyYXRpb25hbF9mb3JfcHJvdGVpbk5QVCwgIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9jc3ZfZm9yX3Byb3RlaW5OUFQvc2F0dXJhdGlvbmFsX2Zvcl9wcm90ZWluTlBULmNzdiIpCgphbGxfZm9yX3Byb3RlaW5OUFQgPC0gdW5pcXVlKHN1YnNldChmaXRuZXNzX2RhdGEsIGZpdG5lc3NfZGF0YSRudW1iZXJfbXV0cz4xKVssYygic2dSTkFfdGFyZ2V0IiwgIm5vcm0iLCAiY29uZGl0aW9uIildKQphbGxfZm9yX3Byb3RlaW5OUFQgPC0gcGl2b3Rfd2lkZXIoYWxsX2Zvcl9wcm90ZWluTlBULCBuYW1lc19mcm9tPWNvbmRpdGlvbiwgdmFsdWVzX2Zyb209bm9ybSkKd3JpdGVfdHN2KGFsbF9mb3JfcHJvdGVpbk5QVCwgIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9jc3ZfZm9yX3Byb3RlaW5OUFQvY29tYmluYXRvcmlhbF9mb3JfcHJvdGVpbk5QVC50c3YiKQpgYGAKCiMgQ3JlYXRlIGZpbGUgZm9yIFN1cHBsZW1lbnQKCmBgYHtyfQpjb2x1bW5zX2Zvcl90YWJsZSA8LSBjKCJzZ1JOQV90YXJnZXQiLCAibnVtX2JhcmNvZGVzIiwgIm51bWJlcl9tdXRzIiwgIm5vcm0iLCAiY29uZGl0aW9uIiwgInBfZml0X2Fkal9XVCIsICJFVmNvdXBfcHJlZGljdCIsICJEZWVwU2VxX3ByZWRpY3QiLCAiTVNBX1RyYW5zZm9ybSIsICJwcm90ZWluTlBUX3ByZWRpY3QiLCAiYWRkaXRpdmVfc2NvcmUiLCAiY29uc2VydmF0aW9uU2NvcmUiLCAiYWJzU0FTIiwgInJlbFNBUyIsICJkaW1lciIpCgp3cml0ZV9jc3YodW5pcXVlKGZpdG5lc3NfZGF0YVssY29sdW1uc19mb3JfdGFibGVdKSwgIi4uL2xmY1NFX3dlaWdodGVkX291dHB1dEltYWdlcy9TdXBwVGFibGVfYWxsX2ZpdG5lc3NfdmFsdWVzLmNzdiIpCmBgYAoKIyBTZXNzaW9uIEluZm8gey51bm51bWJlcmVkfQoKYGBge3Igc2Vzc2lvbkluZm8sIGVjaG89RkFMU0V9CnNlc3Npb25JbmZvKCkKYGBgCgo=